Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

18972 lines
741KB

  1. ;;; lint for s7 scheme
  2. ;;;
  3. ;;; (lint "file.scm") checks file.scm for infelicities
  4. ;;; to control the kinds of checks, set the variables below.
  5. ;;; for tests and examples, see lint-test in s7test.scm
  6. (provide 'lint.scm)
  7. (define *report-unused-parameters* #f) ; many of these are reported anyway if they are passed some non-#f value
  8. (define *report-unused-top-level-functions* #f) ; very common in Scheme, but #t makes the ghastly leakage of names obvious
  9. (define *report-shadowed-variables* #f) ; shadowed parameters, etc
  10. (define *report-undefined-identifiers* #f) ; names we can't account for
  11. (define *report-multiply-defined-top-level-functions* #f) ; top-level funcs defined in more than one file
  12. (define *report-nested-if* 4) ; 3 is lowest, this sets the nesting level that triggers an if->cond suggestion
  13. (define *report-short-branch* 12) ; controls when a lop-sided if triggers a reordering suggestion
  14. (define *report-one-armed-if* 90) ; if -> when/unless, can be #f/#t; if an integer, sets tree length which triggers revision (80 is too small)
  15. (define *report-loaded-files* #f) ; if load is encountered, include that file in the lint process
  16. (define *report-any-!-as-setter* #t) ; unknown funcs/macros ending in ! are treated as setters
  17. (define *report-doc-strings* #f) ; old-style (CL) doc strings
  18. (define *report-func-as-arg-arity-mismatch* #f) ; as it says... (slow, and this error almost never happens)
  19. (define *report-constant-expressions-in-do* #f) ; kinda dumb
  20. (define *report-bad-variable-names* '(l ll O ~)) ; bad names -- a list to check such as:
  21. ;;; '(l ll .. ~ data datum new item info temp tmp temporary val vals value foo bar baz aux dummy O var res retval result count str)
  22. (define *report-built-in-functions-used-as-variables* #f) ; string and length are the most common cases
  23. (define *report-forward-functions* #f) ; functions used before being defined
  24. (define *report-sloppy-assoc* #t) ; i.e. (cdr (assoc x y)) and the like
  25. (define *report-bloated-arg* 24) ; min arg expr tree size that can trigger a rewrite-as-let suggestion (32 is too high I think)
  26. (define *report-clobbered-function-return-value* #f) ; function returns constant sequence, which is then stomped on -- very rare!
  27. (define *report-boolean-functions-misbehaving* #t) ; function name ends in #\? but function returns a non-boolean value -- dubious.
  28. (define *report-repeated-code-fragments* #t)
  29. ;;; work-in-progress
  30. (define *fragments-size* 128) ; biggest seen if 512: 180 -- appears to be in a test suite
  31. (define *report-blocks* #f) ; report huge blocks that could be moved into the closure
  32. (define *lint* #f) ; the lint let
  33. ;; this gives other programs a way to extend or edit lint's tables: for example, the
  34. ;; table of functions that are simple (no side effects) is (*lint* 'no-side-effect-functions)
  35. ;; see snd-lint.scm.
  36. ;;; --------------------------------------------------------------------------------
  37. (when (provided? 'pure-s7)
  38. (define (make-polar mag ang) (complex (* mag (cos ang)) (* mag (sin ang))))
  39. (define (char-ci=? . chars) (apply char=? (map char-upcase chars)))
  40. (define (char-ci<=? . chars) (apply char<=? (map char-upcase chars)))
  41. (define (char-ci>=? . chars) (apply char>=? (map char-upcase chars)))
  42. (define (char-ci<? . chars) (apply char<? (map char-upcase chars)))
  43. (define (char-ci>? . chars) (apply char>? (map char-upcase chars)))
  44. (define (string-ci=? . strs) (apply string=? (map string-upcase strs)))
  45. (define (string-ci<=? . strs) (apply string<=? (map string-upcase strs)))
  46. (define (string-ci>=? . strs) (apply string>=? (map string-upcase strs)))
  47. (define (string-ci<? . strs) (apply string<? (map string-upcase strs)))
  48. (define (string-ci>? . strs) (apply string>? (map string-upcase strs)))
  49. (define (let->list e)
  50. (if (let? e)
  51. (reverse! (map values e))
  52. (error 'wrong-type-arg "let->list argument should be an environment: ~A" str))))
  53. (format *stderr* "loading lint.scm~%")
  54. (set! reader-cond #f)
  55. (define-macro (reader-cond . clauses) `(values)) ; clobber reader-cond to avoid (incorrect) unbound-variable errors
  56. #|
  57. ;; debugging version
  58. (define-expansion (lint-format str caller . args)
  59. `(begin
  60. (format outport "lint.scm line ~A~%" ,(port-line-number))
  61. (lint-format-1 ,str ,caller ,@args)))
  62. (define-expansion (lint-format* caller . args)
  63. `(begin
  64. (format outport "lint.scm line ~A~%" ,(port-line-number))
  65. (lint-format*-1 ,caller ,@args)))
  66. |#
  67. ;;; --------------------------------------------------------------------------------
  68. (define lint
  69. (let ((no-side-effect-functions
  70. (let ((ht (make-hash-table)))
  71. (for-each
  72. (lambda (op)
  73. (hash-table-set! ht op #t))
  74. '(* + - / < <= = > >=
  75. abs acos acosh and angle append aritable? arity ash asin asinh assoc assq assv atan atanh
  76. begin boolean? byte-vector byte-vector?
  77. caaaar caaadr caaar caadar caaddr caadr caar cadaar cadadr cadar caddar cadddr caddr cadr
  78. call-with-input-string call-with-input-file
  79. c-pointer c-pointer? c-object? call-with-exit car case catch cdaaar cdaadr cdaar cdadar cdaddr cdadr cdar cddaar cddadr
  80. cddar cdddar cddddr cdddr cddr cdr ceiling char->integer char-alphabetic? char-ci<=?
  81. char-ci<? char-ci=? char-ci>=? char-ci>? char-downcase char-lower-case? char-numeric?
  82. char-position char-ready? char-upcase char-upper-case? char-whitespace? char<=? char<?
  83. char=? char>=? char>? char? complex complex? cond cons constant? continuation? cos
  84. cosh curlet current-error-port current-input-port current-output-port cyclic-sequences
  85. defined? denominator dilambda? do dynamic-wind
  86. eof-object? eq? equal? eqv? even? exact->inexact exact? exp expt
  87. float? float-vector float-vector-ref float-vector? floor for-each funclet
  88. gcd gensym gensym? ; why was gensym omitted earlier?
  89. hash-table hash-table* hash-table-entries hash-table-ref hash-table? help hook-functions
  90. if imag-part inexact->exact inexact? infinite? inlet input-port?
  91. int-vector int-vector-ref int-vector? iterator-at-end? iterator-sequence integer->char
  92. integer-decode-float integer-length integer? iterator?
  93. keyword->symbol keyword?
  94. lambda lambda* lcm let->list length let let* let-ref let? letrec letrec* list list->string list->vector list-ref
  95. list-tail list? log logand logbit? logior lognot logxor
  96. macro? magnitude make-byte-vector make-float-vector make-int-vector make-hash-table make-hook make-iterator make-keyword make-list make-polar
  97. make-rectangular make-shared-vector make-string make-vector map max member memq memv min modulo morally-equal?
  98. nan? negative? not null? number->string number? numerator
  99. object->string odd? openlet? or outlet output-port? owlet
  100. pair-line-number pair-filename pair? port-closed? port-filename port-line-number positive? procedure-documentation
  101. procedure-setter procedure-signature procedure-source procedure? proper-list? provided?
  102. quasiquote quote quotient
  103. random-state random-state->list random-state? rational? rationalize real-part real? remainder reverse rootlet round
  104. s7-version sequence? sin sinh square sqrt stacktrace string string->list string->number string->symbol string-append
  105. string-ci<=? string-ci<? string-ci=? string-ci>=? string-ci>? string-downcase string-length
  106. string-position string-ref string-upcase string<=? string<? string=? string>=? string>? string?
  107. sublet substring symbol symbol->dynamic-value symbol->keyword symbol->string symbol->value symbol?
  108. tan tanh tree-leaves truncate
  109. unless
  110. values vector vector-append vector->list vector-dimensions vector-length vector-ref vector?
  111. when with-baffle with-let with-input-from-file with-input-from-string with-output-to-string
  112. zero?
  113. #_{list} #_{apply_values} #_{append} unquote))
  114. ;; do not include file-exists? or directory?
  115. ;; should this include peek-char or unlet ?
  116. ht))
  117. (built-in-functions (let ((ht (make-hash-table)))
  118. (for-each
  119. (lambda (op)
  120. (hash-table-set! ht op #t))
  121. '(symbol? gensym? keyword? let? openlet? iterator? constant? macro? c-pointer? c-object?
  122. input-port? output-port? eof-object? integer? number? real? complex? rational? random-state?
  123. char? string? list? pair? vector? float-vector? int-vector? byte-vector? hash-table?
  124. continuation? procedure? dilambda? boolean? float? proper-list? sequence? null? gensym
  125. symbol->string string->symbol symbol symbol->value symbol->dynamic-value symbol-access
  126. make-keyword symbol->keyword keyword->symbol outlet rootlet curlet unlet sublet varlet
  127. cutlet inlet owlet coverlet openlet let-ref let-set! make-iterator iterate iterator-sequence
  128. iterator-at-end? provided? provide defined? c-pointer port-line-number port-filename
  129. pair-line-number pair-filename port-closed? current-input-port current-output-port
  130. current-error-port let->list char-ready? close-input-port close-output-port flush-output-port
  131. open-input-file open-output-file open-input-string open-output-string get-output-string
  132. newline write display read-char peek-char write-char write-string read-byte write-byte
  133. read-line read-string read call-with-input-string call-with-input-file with-input-from-string
  134. with-input-from-file call-with-output-string call-with-output-file with-output-to-string
  135. with-output-to-file real-part imag-part numerator denominator even? odd? zero? positive?
  136. negative? infinite? nan? complex magnitude angle rationalize abs exp log sin cos tan asin
  137. acos atan sinh cosh tanh asinh acosh atanh sqrt expt floor ceiling truncate round lcm gcd
  138. + - * / max min quotient remainder modulo = < > <= >= logior logxor logand lognot ash
  139. random-state random inexact->exact exact->inexact integer-length make-polar make-rectangular
  140. logbit? integer-decode-float exact? inexact? random-state->list number->string string->number
  141. char-upcase char-downcase char->integer integer->char char-upper-case? char-lower-case?
  142. char-alphabetic? char-numeric? char-whitespace? char=? char<? char>? char<=? char>=?
  143. char-position string-position make-string string-ref string-set! string=? string<? string>?
  144. string<=? string>=? char-ci=? char-ci<? char-ci>? char-ci<=? char-ci>=? string-ci=? string-ci<?
  145. string-ci>? string-ci<=? string-ci>=? string-copy string-fill! list->string string-length
  146. string->list string-downcase string-upcase string-append substring string object->string
  147. format cons car cdr set-car! set-cdr! caar cadr cdar cddr caaar caadr cadar cdaar caddr
  148. cdddr cdadr cddar caaaar caaadr caadar cadaar caaddr cadddr cadadr caddar cdaaar cdaadr
  149. cdadar cddaar cdaddr cddddr cddadr cdddar assoc member list list-ref list-set! list-tail
  150. make-list length copy fill! reverse reverse! sort! append assq assv memq memv vector-append
  151. list->vector vector-fill! vector-length vector->list vector-ref vector-set! vector-dimensions
  152. make-vector make-shared-vector vector float-vector make-float-vector float-vector-set!
  153. float-vector-ref int-vector make-int-vector int-vector-set! int-vector-ref string->byte-vector
  154. byte-vector make-byte-vector hash-table hash-table* make-hash-table hash-table-ref
  155. hash-table-set! hash-table-entries cyclic-sequences call/cc call-with-current-continuation
  156. call-with-exit load autoload eval eval-string apply for-each map dynamic-wind values
  157. catch throw error procedure-documentation procedure-signature help procedure-source funclet
  158. procedure-setter arity aritable? not eq? eqv? equal? morally-equal? gc s7-version emergency-exit
  159. exit dilambda make-hook hook-functions stacktrace tree-leaves object->let
  160. #_{list} #_{apply_values} #_{append} unquote))
  161. ht))
  162. (makers (let ((h (make-hash-table)))
  163. (for-each
  164. (lambda (op)
  165. (set! (h op) #t))
  166. '(gensym sublet inlet make-iterator let->list random-state random-state->list number->string object->let
  167. make-string string string-copy copy list->string string->list string-append substring object->string
  168. format cons list make-list reverse append vector-append list->vector vector->list make-vector
  169. make-shared-vector vector make-float-vector float-vector make-int-vector int-vector byte-vector
  170. hash-table hash-table* make-hash-table make-hook #_{list} #_{append} gentemp)) ; gentemp for other schemes
  171. h))
  172. (non-negative-ops (let ((h (make-hash-table)))
  173. (for-each
  174. (lambda (op)
  175. (set! (h op) #t))
  176. '(string-length vector-length abs magnitude denominator gcd lcm tree-leaves
  177. char->integer byte-vector-ref byte-vector-set! hash-table-entries write-byte
  178. char-position string-position pair-line-number port-line-number))
  179. h))
  180. (numeric-ops (let ((h (make-hash-table)))
  181. (for-each
  182. (lambda (op)
  183. (set! (h op) #t))
  184. '(+ * - /
  185. sin cos tan asin acos atan sinh cosh tanh asinh acosh atanh
  186. log exp expt sqrt make-polar complex
  187. imag-part real-part abs magnitude angle max min exact->inexact
  188. modulo remainder quotient lcm gcd
  189. rationalize inexact->exact random
  190. logior lognot logxor logand numerator denominator
  191. floor round truncate ceiling ash))
  192. h))
  193. (bools (let ((h (make-hash-table)))
  194. (for-each
  195. (lambda (op)
  196. (set! (h op) #t))
  197. '(symbol? integer? rational? real? number? complex? float? keyword? gensym? byte-vector? string? list? sequence?
  198. char? boolean? float-vector? int-vector? vector? let? hash-table? input-port? null? pair? proper-list?
  199. output-port? iterator? continuation? dilambda? procedure? macro? random-state? eof-object? c-pointer?
  200. unspecified? c-object? constant?))
  201. h))
  202. (booleans (let ((h (make-hash-table)))
  203. (for-each
  204. (lambda (op)
  205. (set! (h op) #t))
  206. '(symbol? integer? rational? real? number? complex? float? keyword? gensym? byte-vector? string? list? sequence?
  207. char? boolean? float-vector? int-vector? vector? let? hash-table? input-port? null? pair? proper-list?
  208. output-port? iterator? continuation? dilambda? procedure? macro? random-state? eof-object? c-pointer? c-object?
  209. unspecified? exact? inexact? defined? provided? even? odd? char-whitespace? char-numeric? char-alphabetic?
  210. negative? positive? zero? constant? infinite? nan? char-upper-case? char-lower-case? directory? file-exists?))
  211. h))
  212. (reversibles (let ((h (make-hash-table)))
  213. (for-each
  214. (lambda (op)
  215. (set! (h (car op)) (cadr op)))
  216. '((< >) (> <) (<= >=) (>= <=)
  217. (* *) (+ +) (= =) (char=? char=?) (string=? string=?)
  218. (eq? eq?) (eqv? eqv?) (equal? equal?) (morally-equal? morally-equal?)
  219. (logand logand) (logxor logxor) (logior logior)
  220. (max max) (min min) (lcm lcm) (gcd gcd)
  221. (char<? char>?) (char>? char<?) (char<=? char>=?) (char>=? char<=?)
  222. (string<? string>?) (string>? string<?) (string<=? string>=?) (string>=? string<=?)
  223. (char-ci<? char-ci>?) (char-ci>? char-ci<?) (char-ci<=? char-ci>=?) (char-ci>=? char-ci<=?)
  224. (string-ci<? string-ci>?) (string-ci>? string-ci<?) (string-ci<=? string-ci>=?) (string-ci>=? string-ci<=?)))
  225. h))
  226. (syntaces (let ((h (make-hash-table)))
  227. (for-each
  228. (lambda (op)
  229. (set! (h op) #t))
  230. '(quote if begin let let* letrec letrec* cond case or and do set! unless when
  231. with-let with-baffle
  232. lambda lambda* define define*
  233. define-macro define-macro* define-bacro define-bacro*
  234. define-constant define-expansion))
  235. h))
  236. (outport #t)
  237. (linted-files ())
  238. (big-constants (make-hash-table))
  239. (other-names-counts (make-hash-table))
  240. (*e* #f)
  241. (other-identifiers (make-hash-table))
  242. (quote-warnings 0)
  243. (last-simplify-boolean-line-number -1)
  244. (last-simplify-numeric-line-number -1)
  245. (last-simplify-cxr-line-number -1)
  246. (last-if-line-number -1)
  247. (last-checker-line-number -1)
  248. (last-cons-line-number -1)
  249. (last-rewritten-internal-define #f)
  250. (line-number -1)
  251. (pp-left-margin 4)
  252. (lint-left-margin 1)
  253. (*current-file* "")
  254. (*top-level-objects* (make-hash-table))
  255. (*output-port* *stderr*)
  256. (fragments (let ((v (make-vector *fragments-size* #f)))
  257. (do ((i 0 (+ i 1)))
  258. ((= i *fragments-size*))
  259. (set! (v i) (make-hash-table)))
  260. v))
  261. (*max-cdr-len* 16)) ; 40 is too high, 24 questionable, if #f the let+do rewrite is turned off
  262. (set! *e* (curlet))
  263. (set! *lint* *e*) ; external access to (for example) the built-in-functions hash-table via (*lint* 'built-in-functions)
  264. ;; -------- lint-format --------
  265. (define target-line-length 80)
  266. (define (truncated-list->string form)
  267. ;; return form -> string with limits on its length
  268. (let* ((str (object->string form))
  269. (len (length str)))
  270. (if (< len target-line-length)
  271. str
  272. (do ((i (- target-line-length 6) (- i 1)))
  273. ((or (= i 40)
  274. (char-whitespace? (str i)))
  275. (string-append (substring str 0 (if (<= i 40)
  276. (- target-line-length 6)
  277. i))
  278. "..."))))))
  279. (define lint-pp #f) ; avoid crosstalk with other schemes' definitions of pp and pretty-print (make-var also collides)
  280. (define lint-pretty-print #f)
  281. (let ()
  282. (require write.scm)
  283. (set! lint-pp pp);
  284. (set! lint-pretty-print pretty-print))
  285. (define (lists->string f1 f2)
  286. ;; same but 2 strings that may need to be lined up vertically
  287. (let ((str1 (object->string f1))
  288. (str2 (object->string f2)))
  289. (let ((len1 (length str1))
  290. (len2 (length str2)))
  291. (when (> len1 target-line-length)
  292. (set! str1 (truncated-list->string f1))
  293. (set! len1 (length str1)))
  294. (when (> len2 target-line-length)
  295. (set! ((funclet lint-pretty-print) '*pretty-print-left-margin*) pp-left-margin)
  296. (set! ((funclet lint-pretty-print) '*pretty-print-length*) (- 114 pp-left-margin))
  297. (set! str2 (lint-pp f2))
  298. (set! len2 (length str2)))
  299. (format #f (if (< (+ len1 len2) target-line-length)
  300. (values "~A -> ~A" str1 str2)
  301. (values "~%~NC~A ->~%~NC~A" pp-left-margin #\space str1 pp-left-margin #\space str2))))))
  302. (define (truncated-lists->string f1 f2)
  303. ;; same but 2 strings that may need to be lined up vertically and both are truncated
  304. (let ((str1 (object->string f1))
  305. (str2 (object->string f2)))
  306. (let ((len1 (length str1))
  307. (len2 (length str2)))
  308. (when (> len1 target-line-length)
  309. (set! str1 (truncated-list->string f1))
  310. (set! len1 (length str1)))
  311. (when (> len2 target-line-length)
  312. (set! str2 (truncated-list->string f2))
  313. (set! len2 (length str2)))
  314. (format #f (if (< (+ len1 len2) target-line-length)
  315. (values "~A -> ~A" str1 str2)
  316. (values "~%~NC~A ->~%~NC~A" pp-left-margin #\space str1 pp-left-margin #\space str2))))))
  317. (define made-suggestion 0)
  318. (define (lint-format str caller . args)
  319. (let ((outstr (apply format #f
  320. (string-append (if (< 0 line-number 100000)
  321. "~NC~A (line ~D): "
  322. "~NC~A: ")
  323. str "~%")
  324. lint-left-margin #\space
  325. (truncated-list->string caller)
  326. (if (< 0 line-number 100000)
  327. (values line-number args)
  328. args))))
  329. (set! made-suggestion (+ made-suggestion 1))
  330. (display outstr outport)
  331. (if (> (length outstr) (+ target-line-length 40))
  332. (newline outport))))
  333. (define (lint-format* caller . strs)
  334. (let* ((outstr (format #f
  335. (if (< 0 line-number 100000)
  336. "~NC~A (line ~D): "
  337. "~NC~A:~A")
  338. lint-left-margin #\space
  339. (truncated-list->string caller)
  340. (if (< 0 line-number 100000)
  341. line-number
  342. " ")))
  343. (current-end (length outstr)))
  344. ;; (set! made-suggestion (+ made-suggestion 1))
  345. (display outstr outport)
  346. (for-each (lambda (s)
  347. (let ((len (length s)))
  348. (if (> (+ len current-end) target-line-length)
  349. (begin
  350. (format outport "~%~NC~A" (+ lint-left-margin 4) #\space s)
  351. (set! current-end len))
  352. (begin
  353. (display s outport)
  354. (set! current-end (+ current-end len))))))
  355. strs)
  356. (newline outport)))
  357. (define (local-line-number tree)
  358. (let ((tree-line (if (pair? tree) (pair-line-number tree) 0)))
  359. (if (and (< 0 tree-line 100000)
  360. (not (= tree-line line-number)))
  361. (format #f " (line ~D)" tree-line)
  362. "")))
  363. ;; -------- vars --------
  364. (define var-name car)
  365. (define (var? v) (and (pair? v) (let? (cdr v))))
  366. (define var-member assq)
  367. (define var-ref (dilambda (lambda (v) (let-ref (cdr v) 'ref)) (lambda (v x) (let-set! (cdr v) 'ref x))))
  368. (define var-set (dilambda (lambda (v) (let-ref (cdr v) 'set)) (lambda (v x) (let-set! (cdr v) 'set x))))
  369. (define var-history (dilambda (lambda (v) (let-ref (cdr v) 'history)) (lambda (v x) (let-set! (cdr v) 'history x))))
  370. (define var-ftype (dilambda (lambda (v) (let-ref (cdr v) 'ftype)) (lambda (v x) (let-set! (cdr v) 'ftype x))))
  371. (define var-retcons (dilambda (lambda (v) (let-ref (cdr v) 'retcons)) (lambda (v x) (let-set! (cdr v) 'retcons x))))
  372. (define var-arglist (dilambda (lambda (v) (let-ref (cdr v) 'arglist)) (lambda (v x) (let-set! (cdr v) 'arglist x))))
  373. (define var-definer (dilambda (lambda (v) (let-ref (cdr v) 'definer)) (lambda (v x) (let-set! (cdr v) 'definer x))))
  374. (define var-leaves (dilambda (lambda (v) (let-ref (cdr v) 'leaves)) (lambda (v x) (let-set! (cdr v) 'leaves x))))
  375. (define var-scope (dilambda (lambda (v) (let-ref (cdr v) 'scope)) (lambda (v x) (let-set! (cdr v) 'scope x))))
  376. (define var-setters (dilambda (lambda (v) (let-ref (cdr v) 'setters)) (lambda (v x) (let-set! (cdr v) 'setters x))))
  377. (define var-env (dilambda (lambda (v) (let-ref (cdr v) 'env)) (lambda (v x) (let-set! (cdr v) 'env x))))
  378. (define var-decl (dilambda (lambda (v) (let-ref (cdr v) 'decl)) (lambda (v x) (let-set! (cdr v) 'decl x))))
  379. (define var-match-list (dilambda (lambda (v) (let-ref (cdr v) 'match-list)) (lambda (v x) (let-set! (cdr v) 'match-list x))))
  380. (define var-initial-value (lambda (v) (let-ref (cdr v) 'initial-value))) ; not (easily) settable
  381. (define var-side-effect (dilambda (lambda (v)
  382. (if (null? (let-ref (cdr v) 'side-effect))
  383. (let-set! (cdr v) 'side-effect (get-side-effect v))
  384. (let-ref (cdr v) 'side-effect)))
  385. (lambda (v x)
  386. (let-set! (cdr v) 'side-effect x))))
  387. (define var-signature (dilambda (lambda (v)
  388. (if (null? (let-ref (cdr v) 'signature))
  389. (let-set! (cdr v) 'signature (get-signature v))
  390. (let-ref (cdr v) 'signature)))
  391. (lambda (v x)
  392. (let-set! (cdr v) 'signature x))))
  393. (define* (make-var name initial-value definer)
  394. (let ((old (hash-table-ref other-identifiers name)))
  395. (cons name (inlet 'initial-value initial-value
  396. 'definer definer
  397. 'history (if old
  398. (begin
  399. (hash-table-set! other-identifiers name #f)
  400. (if initial-value (cons initial-value old) old))
  401. (if initial-value (list initial-value) ()))
  402. 'scope ()
  403. 'setters ()
  404. 'set 0
  405. 'ref (if old (length old) 0)))))
  406. ;; -------- the usual list functions --------
  407. (define (remove item sequence)
  408. (cond ((null? sequence) ())
  409. ((equal? item (car sequence)) (cdr sequence))
  410. (else (cons (car sequence) (remove item (cdr sequence))))))
  411. (define (remove-all item sequence)
  412. (map (lambda (x)
  413. (if (equal? x item)
  414. (values)
  415. x))
  416. sequence))
  417. (define (remove-if p lst)
  418. (cond ((null? lst) ())
  419. ((p (car lst)) (remove-if p (cdr lst)))
  420. (else (cons (car lst)
  421. (remove-if p (cdr lst))))))
  422. (define (lint-remove-duplicates lst env)
  423. (reverse (let rem-dup ((lst lst)
  424. (nlst ()))
  425. (cond ((null? lst) nlst)
  426. ((and (member (car lst) nlst)
  427. (not (and (pair? (car lst))
  428. (side-effect? (car lst) env))))
  429. (rem-dup (cdr lst) nlst))
  430. (else (rem-dup (cdr lst) (cons (car lst) nlst)))))))
  431. (define applicable? arity)
  432. (define every?
  433. (let ((documentation "(every? func sequence) returns #t if func approves of every member of sequence")
  434. (signature '(boolean? procedure? sequence?)))
  435. (lambda (f sequence)
  436. (call-with-exit
  437. (lambda (return)
  438. (for-each (lambda (arg) (if (not (f arg)) (return #f))) sequence)
  439. #t)))))
  440. (define any?
  441. (let ((documentation "(any? func sequence) returns #t if func approves of any member of sequence")
  442. (signature '(boolean? procedure? sequence?)))
  443. (lambda (f sequence)
  444. (call-with-exit
  445. (lambda (return)
  446. (for-each (lambda (arg) (if (f arg) (return #t))) sequence)
  447. #f)))))
  448. (define collect-if
  449. (let ((documentation "(collect-if type func sequence) gathers the elements of sequence that satisfy func, and returns them via type:\n\
  450. (collect-if list integer? #(1.4 2/3 1 1+i 2)) -> '(1 2)"))
  451. (lambda (type f sequence)
  452. (apply type (map (lambda (arg) (if (f arg) arg (values))) sequence)))))
  453. (define find-if
  454. (let ((documentation "(find-if func sequence) applies func to each member of sequence.\n\
  455. If func approves of one, find-if returns that member of the sequence"))
  456. (lambda (f sequence)
  457. (call-with-exit
  458. (lambda (return)
  459. (for-each (lambda (arg)
  460. (if (f arg)
  461. (return arg)))
  462. sequence)
  463. #f)))))
  464. ;; -------- trees --------
  465. (define copy-tree
  466. (let ((documentation "(copy-tree lst) returns a full copy of lst"))
  467. (lambda (lis)
  468. (if (pair? lis)
  469. (cons (copy-tree (car lis))
  470. (copy-tree (cdr lis)))
  471. lis))))
  472. (define (tree-count1 x tree count)
  473. (if (eq? x tree)
  474. (+ count 1)
  475. (if (or (>= count 2)
  476. (not (pair? tree))
  477. (eq? (car tree) 'quote))
  478. count
  479. (tree-count1 x (car tree) (tree-count1 x (cdr tree) count)))))
  480. (define (tree-count2 x tree count)
  481. (if (eq? x tree)
  482. (+ count 1)
  483. (if (or (>= count 3)
  484. (not (pair? tree))
  485. (eq? (car tree) 'quote))
  486. count
  487. (tree-count2 x (car tree) (tree-count2 x (cdr tree) count)))))
  488. (define (proper-tree? tree)
  489. (or (not (pair? tree))
  490. (and (proper-list? tree)
  491. (every? proper-tree? (cdr tree)))))
  492. (define (gather-symbols tree)
  493. (let ((syms ()))
  494. (let walk ((p tree))
  495. (if (pair? p)
  496. (if (symbol? (car p))
  497. (if (not (eq? (car p) 'quote))
  498. (for-each (lambda (a)
  499. (if (symbol? a)
  500. (if (not (memq a syms))
  501. (set! syms (cons a syms)))
  502. (if (pair? a) (walk a))))
  503. (cdr p)))
  504. (if (pair? (car p))
  505. (begin
  506. (walk (car p))
  507. (walk (cdr p)))))
  508. (if (and (symbol? tree)
  509. (not (memq tree syms)))
  510. (set! syms (cons tree syms)))))
  511. syms))
  512. (define (tree-arg-member sym tree)
  513. (and (proper-list? tree)
  514. (or (and (memq sym (cdr tree))
  515. tree)
  516. (and (pair? (car tree))
  517. (tree-arg-member sym (car tree)))
  518. (and (pair? (cdr tree))
  519. (call-with-exit
  520. (lambda (return)
  521. (for-each
  522. (lambda (b)
  523. (cond ((and (pair? b)
  524. (tree-arg-member sym b))
  525. => return)))
  526. (cdr tree))
  527. #f))))))
  528. (define (tree-memq sym tree) ; ignore quoted lists, accept symbol outside a pair
  529. (or (eq? sym tree)
  530. (and (pair? tree)
  531. (not (eq? (car tree) 'quote))
  532. (or (eq? (car tree) sym)
  533. (tree-memq sym (car tree))
  534. (tree-memq sym (cdr tree))))))
  535. (define (tree-member sym tree)
  536. (and (pair? tree)
  537. (or (eq? (car tree) sym)
  538. (tree-member sym (car tree))
  539. (tree-member sym (cdr tree)))))
  540. (define (tree-equal-member sym tree)
  541. (and (pair? tree)
  542. (or (equal? (car tree) sym)
  543. (tree-member sym (car tree))
  544. (tree-member sym (cdr tree)))))
  545. (define (tree-unquoted-member sym tree)
  546. (and (pair? tree)
  547. (not (eq? (car tree) 'quote))
  548. (or (eq? (car tree) sym)
  549. (tree-unquoted-member sym (car tree))
  550. (tree-unquoted-member sym (cdr tree)))))
  551. (define (tree-car-member sym tree)
  552. (and (pair? tree)
  553. (or (eq? (car tree) sym)
  554. (and (pair? (car tree))
  555. (tree-car-member sym (car tree)))
  556. (and (pair? (cdr tree))
  557. (member sym (cdr tree) tree-car-member)))))
  558. (define (tree-sym-set-member sym set tree) ; sym as arg, set as car
  559. (and (pair? tree)
  560. (or (memq (car tree) set)
  561. (and (pair? (car tree))
  562. (tree-sym-set-member sym set (car tree)))
  563. (and (pair? (cdr tree))
  564. (or (member sym (cdr tree))
  565. (member #f (cdr tree) (lambda (a b) (tree-sym-set-member sym set b))))))))
  566. (define (tree-set-member set tree)
  567. (and (pair? tree)
  568. (not (eq? (car tree) 'quote))
  569. (or (memq (car tree) set)
  570. (tree-set-member set (car tree))
  571. (tree-set-member set (cdr tree)))))
  572. (define (tree-table-member table tree)
  573. (and (pair? tree)
  574. (or (hash-table-ref table (car tree))
  575. (tree-table-member table (car tree))
  576. (tree-table-member table (cdr tree)))))
  577. (define (tree-set-car-member set tree) ; set as car
  578. (and (pair? tree)
  579. (or (and (memq (car tree) set)
  580. tree)
  581. (and (pair? (car tree))
  582. (tree-set-car-member set (car tree)))
  583. (and (pair? (cdr tree))
  584. (member #f (cdr tree) (lambda (a b) (tree-set-car-member set b)))))))
  585. (define (tree-table-car-member set tree) ; hash-table as car
  586. (and (pair? tree)
  587. (or (and (hash-table-ref set (car tree))
  588. tree)
  589. (and (pair? (car tree))
  590. (tree-table-car-member set (car tree)))
  591. (and (pair? (cdr tree))
  592. (member #f (cdr tree) (lambda (a b) (tree-table-car-member set b)))))))
  593. (define (maker? tree)
  594. (tree-table-car-member makers tree))
  595. (define (tree-symbol-walk tree syms)
  596. (if (pair? tree)
  597. (if (eq? (car tree) 'quote)
  598. (if (and (pair? (cdr tree))
  599. (symbol? (cadr tree))
  600. (not (memq (cadr tree) (car syms))))
  601. (tree-symbol-walk (cddr tree) (begin (set-car! syms (cons (cadr tree) (car syms))) syms)))
  602. (if (eq? (car tree) {list})
  603. (if (and (pair? (cdr tree))
  604. (pair? (cadr tree))
  605. (eq? (caadr tree) 'quote)
  606. (symbol? (cadadr tree))
  607. (not (memq (cadadr tree) (cadr syms))))
  608. (tree-symbol-walk (cddr tree) (begin (list-set! syms 1 (cons (cadadr tree) (cadr syms))) syms)))
  609. (begin
  610. (tree-symbol-walk (car tree) syms)
  611. (tree-symbol-walk (cdr tree) syms))))))
  612. ;; -------- types --------
  613. (define (quoted-undotted-pair? x)
  614. (and (pair? x)
  615. (eq? (car x) 'quote)
  616. (pair? (cdr x))
  617. (pair? (cadr x))
  618. (positive? (length (cadr x)))))
  619. (define (quoted-null? x)
  620. (and (pair? x)
  621. (eq? (car x) 'quote)
  622. (pair? (cdr x))
  623. (null? (cadr x))))
  624. (define (any-null? x)
  625. (or (null? x)
  626. (and (pair? x)
  627. (case (car x)
  628. ((quote)
  629. (and (pair? (cdr x))
  630. (null? (cadr x))))
  631. ((list)
  632. (null? (cdr x)))
  633. (else #f)))))
  634. (define (quoted-not? x)
  635. (and (pair? x)
  636. (eq? (car x) 'quote)
  637. (pair? (cdr x))
  638. (not (cadr x))))
  639. (define (quoted-symbol? x)
  640. (and (pair? x)
  641. (eq? (car x) 'quote)
  642. (pair? (cdr x))
  643. (symbol? (cadr x))))
  644. (define (code-constant? x)
  645. (and (or (not (symbol? x))
  646. (keyword? x))
  647. (or (not (pair? x))
  648. (eq? (car x) 'quote))))
  649. (define (just-symbols? form)
  650. (or (null? form)
  651. (symbol? form)
  652. (and (pair? form)
  653. (symbol? (car form))
  654. (just-symbols? (cdr form)))))
  655. (define (list-any? f lst)
  656. (if (pair? lst)
  657. (or (f (car lst))
  658. (list-any? f (cdr lst)))
  659. (f lst)))
  660. (define syntax?
  661. (let ((syns (let ((h (make-hash-table)))
  662. (for-each (lambda (x)
  663. (hash-table-set! h x #t))
  664. (list quote if when unless begin set! let let* letrec letrec* cond and or case do
  665. lambda lambda* define define* define-macro define-macro* define-bacro define-bacro*
  666. define-constant with-baffle macroexpand with-let))
  667. h)))
  668. (lambda (obj) ; a value, not a symbol
  669. (hash-table-ref syns obj))))
  670. ;; -------- func info --------
  671. (define (arg-signature fnc env)
  672. (and (symbol? fnc)
  673. (let ((fd (var-member fnc env)))
  674. (if (var? fd)
  675. (and (symbol? (var-ftype fd))
  676. (var-signature fd))
  677. (or (and (eq? *e* *lint*)
  678. (procedure-signature fnc))
  679. (let ((f (symbol->value fnc *e*)))
  680. (and (procedure? f)
  681. (procedure-signature f))))))))
  682. (define (arg-arity fnc env)
  683. (and (symbol? fnc)
  684. (let ((fd (var-member fnc env)))
  685. (if (var? fd)
  686. (and (not (eq? (var-decl fd) 'error))
  687. (arity (var-decl fd)))
  688. (let ((f (symbol->value fnc *e*)))
  689. (and (procedure? f)
  690. (arity f)))))))
  691. (define (dummy-func caller form f)
  692. (catch #t
  693. (lambda ()
  694. (eval f))
  695. (lambda args
  696. (lint-format* caller
  697. (string-append "in " (truncated-list->string form) ", ")
  698. (apply format #f (cadr args))))))
  699. (define (count-values body)
  700. (let ((mn #f)
  701. (mx #f))
  702. (if (pair? body)
  703. (let counter ((ignored #f) ; 'ignored is for member's benefit
  704. (tree (list-ref body (- (length body) 1))))
  705. (if (pair? tree)
  706. (if (eq? (car tree) 'values)
  707. (let ((args (- (length tree) 1)))
  708. (for-each (lambda (p)
  709. (if (and (pair? p) (eq? (car p) 'values))
  710. (set! args (- (+ (args (length p)) 2)))))
  711. (cdr tree))
  712. (set! mn (min (or mn args) args))
  713. (set! mx (max (or mx args) args)))
  714. (begin
  715. (if (pair? (car tree))
  716. (counter 'values (car tree)))
  717. (if (pair? (cdr tree))
  718. (member #f (cdr tree) counter)))))
  719. #f)) ; return #f so member doesn't quit early
  720. (and mn (list mn mx))))
  721. (define (get-signature v)
  722. (define (signer endb env)
  723. (and (not (side-effect? endb env))
  724. (cond ((not (pair? endb))
  725. (and (not (symbol? endb))
  726. (list (->lint-type endb))))
  727. ((arg-signature (car endb) env)
  728. => (lambda (a)
  729. (and (pair? a)
  730. (list (car a)))))
  731. ((and (eq? (car endb) 'if)
  732. (pair? (cddr endb)))
  733. (let ((a1 (signer (caddr endb) env))
  734. (a2 (and (pair? (cdddr endb))
  735. (signer (cadddr endb) env))))
  736. (if (not a2)
  737. a1
  738. (and (equal? a1 a2) a1))))
  739. (else #f))))
  740. (let ((ftype (var-ftype v))
  741. (initial-value (var-initial-value v))
  742. (arglist (var-arglist v))
  743. (env (var-env v)))
  744. (let ((body (and (memq ftype '(define define* lambda lambda* let))
  745. (cddr initial-value))))
  746. (and (pair? body)
  747. (let ((sig (signer (list-ref body (- (length body) 1)) env)))
  748. (if (not (pair? sig))
  749. (set! sig (list #t)))
  750. (when (and (proper-list? arglist)
  751. (not (any? keyword? arglist)))
  752. (for-each
  753. (lambda (arg) ; new function's parameter
  754. (set! sig (cons #t sig))
  755. ;; (if (pair? arg) (set! arg (car arg)))
  756. ;; causes trouble when tree-count1 sees keyword args in s7test.scm
  757. (if (= (tree-count1 arg body 0) 1)
  758. (let ((p (tree-arg-member arg body)))
  759. (when (pair? p)
  760. (let ((f (car p))
  761. (m (memq arg (cdr p))))
  762. (if (pair? m)
  763. (let ((fsig (arg-signature f env)))
  764. (if (pair? fsig)
  765. (let ((chk (catch #t (lambda () (fsig (- (length p) (length m)))) (lambda args #f))))
  766. (if (and (symbol? chk) ; it defaults to #t
  767. (not (memq chk '(integer:any? integer:real?))))
  768. (set-car! sig chk)))))))))))
  769. arglist))
  770. (and (any? (lambda (a) (not (eq? a #t))) sig)
  771. (reverse sig)))))))
  772. (define (args->proper-list args)
  773. (cond ((symbol? args) (list args))
  774. ((not (pair? args)) args)
  775. ((pair? (car args)) (cons (caar args) (args->proper-list (cdr args))))
  776. (else (cons (car args) (args->proper-list (cdr args))))))
  777. (define (out-vars func-name arglist body)
  778. (let ((ref ())
  779. (set ()))
  780. (let var-walk ((tree body)
  781. (e (cons func-name arglist)))
  782. (define (var-walk-body tree e)
  783. (when (pair? tree)
  784. (for-each (lambda (p) (set! e (var-walk p e))) tree)))
  785. (define (shadowed v)
  786. (if (and (or (memq v e) (memq v ref))
  787. (not (memq v set)))
  788. (set! set (cons v set)))
  789. v)
  790. (if (symbol? tree)
  791. (if (not (or (memq tree e) (memq tree ref) (defined? tree (rootlet))))
  792. (set! ref (cons tree ref)))
  793. (when (pair? tree)
  794. (if (not (pair? (cdr tree)))
  795. (var-walk (car tree) e)
  796. (case (car tree)
  797. ((set! vector-set! list-set! hash-table-set! float-vector-set! int-vector-set!
  798. string-set! let-set! fill! string-fill! list-fill! vector-fill!
  799. reverse! sort! set-car! set-cdr!)
  800. (let ((sym (if (symbol? (cadr tree))
  801. (cadr tree)
  802. (if (pair? (cadr tree)) (caadr tree)))))
  803. (if (not (or (memq sym e) (memq sym set)))
  804. (set! set (cons sym set)))
  805. (var-walk (cddr tree) e)))
  806. ((let letrec)
  807. (if (and (pair? (cdr tree))
  808. (pair? (cddr tree)))
  809. (let* ((named (symbol? (cadr tree)))
  810. (vars (if named
  811. (list (shadowed (cadr tree)))
  812. ()))
  813. (varlist ((if named caddr cadr) tree)))
  814. (when (pair? varlist)
  815. (for-each (lambda (v)
  816. (when (and (pair? v)
  817. (pair? (cdr v)))
  818. (var-walk (cadr v) e)
  819. (set! vars (cons (shadowed (car v)) vars))))
  820. ((if named caddr cadr) tree)))
  821. (var-walk-body ((if named cdddr cddr) tree) (append vars e)))))
  822. ((let* letrec*)
  823. (let* ((named (symbol? (cadr tree)))
  824. (vars (if named (list (cadr tree)) ()))
  825. (varlist ((if named caddr cadr) tree)))
  826. (when (pair? varlist)
  827. (for-each (lambda (v)
  828. (when (and (pair? v)
  829. (pair? (cdr v)))
  830. (var-walk (cadr v) (append vars e))
  831. (set! vars (cons (shadowed (car v)) vars))))
  832. varlist))
  833. (var-walk-body ((if named cdddr cddr) tree) (append vars e))))
  834. ((case)
  835. (when (and (pair? (cdr tree))
  836. (pair? (cddr tree)))
  837. (for-each (lambda (c)
  838. (when (pair? c)
  839. (var-walk (cdr c) e)))
  840. (cddr tree))))
  841. ((quote) #f)
  842. ((do)
  843. (let ((vars ()))
  844. (when (pair? (cadr tree))
  845. (for-each (lambda (v)
  846. (when (and (pair? v)
  847. (pair? (cdr v)))
  848. (var-walk (cadr v) e)
  849. (set! vars (cons (shadowed (car v)) vars))))
  850. (cadr tree))
  851. (for-each (lambda (v)
  852. (if (and (pair? v)
  853. (pair? (cdr v))
  854. (pair? (cddr v)))
  855. (var-walk (caddr v) (append vars e))))
  856. (cadr tree)))
  857. (when (pair? (cddr tree))
  858. (var-walk (caddr tree) (append vars e))
  859. (var-walk-body (cdddr tree) (append vars e)))))
  860. ((lambda lambda*)
  861. (var-walk-body (cddr tree) (append (args->proper-list (cadr tree)) e)))
  862. ((define* define-macro define-macro* define-bacro define-bacro*)
  863. (if (and (pair? (cdr tree))
  864. (pair? (cddr tree)))
  865. (begin
  866. (set! e (cons (caadr tree) e))
  867. (var-walk-body (cddr tree) (append (args->proper-list (cdadr tree)) e)))))
  868. ((define define-constant)
  869. (if (and (pair? (cdr tree))
  870. (pair? (cddr tree)))
  871. (if (symbol? (cadr tree))
  872. (begin
  873. (var-walk (caddr tree) e)
  874. (set! e (cons (cadr tree) e)))
  875. (begin
  876. (set! e (cons (caadr tree) e))
  877. (var-walk-body (cddr tree) (append (args->proper-list (cdadr tree)) e))))))
  878. (else
  879. (var-walk (car tree) e)
  880. (var-walk (cdr tree) e))))))
  881. e)
  882. (list ref set)))
  883. (define (get-side-effect v)
  884. (let ((ftype (var-ftype v)))
  885. (or (not (memq ftype '(define define* lambda lambda*)))
  886. (let ((body (cddr (var-initial-value v)))
  887. (env (var-env v))
  888. (args (cons (var-name v) (args->proper-list (var-arglist v)))))
  889. (let ((outvars (append (cadr (out-vars (var-name v) args body)) args)))
  890. (any? (lambda (f)
  891. (side-effect-with-vars? f env outvars))
  892. body))))))
  893. (define (last-par x)
  894. (let ((len (length x)))
  895. (and (positive? len)
  896. (x (- len 1)))))
  897. (define* (make-fvar name ftype arglist decl initial-value env)
  898. ;(format *stderr* "fvar: ~A~%" name)
  899. (let ((new (let ((old (hash-table-ref other-identifiers name)))
  900. (cons name
  901. (inlet 'signature ()
  902. 'side-effect ()
  903. 'allow-other-keys (and (pair? arglist)
  904. (memq ftype '(define* define-macro* define-bacro* defmacro*))
  905. (eq? (last-par arglist) :allow-other-keys))
  906. 'scope ()
  907. 'setters ()
  908. 'env env
  909. 'initial-value initial-value
  910. 'values (and (pair? initial-value) (count-values (cddr initial-value)))
  911. 'leaves #f
  912. 'match-list #f
  913. 'decl decl
  914. 'arglist arglist
  915. 'ftype ftype
  916. 'retcons #f
  917. 'history (if old
  918. (begin
  919. (hash-table-set! other-identifiers name #f)
  920. (if initial-value (cons initial-value old) old))
  921. (if initial-value (list initial-value) ()))
  922. 'set 0
  923. 'ref (if old (length old) 0))))))
  924. (reduce-function-tree new env)
  925. new))
  926. (define (return-type sym e)
  927. (let ((sig (arg-signature sym e)))
  928. (and (pair? sig)
  929. (or (eq? (car sig) 'values) ; turn it into #t for now
  930. (car sig))))) ; this might be undefined in the current context (eg oscil? outside clm)
  931. (define any-macro?
  932. (let ((macros (let ((h (make-hash-table)))
  933. (for-each
  934. (lambda (m)
  935. (set! (h m) #t))
  936. '(call-with-values let-values define-values let*-values cond-expand require quasiquote
  937. multiple-value-bind reader-cond match while))
  938. h)))
  939. (lambda (f env)
  940. (or (hash-table-ref macros f)
  941. (let ((fd (var-member f env)))
  942. (and (var? fd)
  943. (memq (var-ftype fd) '(define-macro define-macro* define-expansion
  944. define-bacro define-bacro* defmacro defmacro* define-syntax))))))))
  945. (define (any-procedure? f env)
  946. (or (hash-table-ref built-in-functions f)
  947. (let ((v (var-member f env)))
  948. (and (var? v)
  949. (memq (var-ftype v) '(define define* lambda lambda*))))))
  950. (define ->simple-type
  951. (let ((markers (list (cons :call/exit 'continuation?)
  952. (cons :call/cc 'continuation?)
  953. (cons :dilambda 'dilambda?)
  954. (cons :lambda 'procedure?))))
  955. (lambda (c)
  956. (cond ((pair? c) 'pair?)
  957. ((integer? c) 'integer?)
  958. ((rational? c) 'rational?)
  959. ((real? c) 'real?)
  960. ((number? c) 'number?)
  961. ((string? c) 'string?)
  962. ((null? c) 'null?)
  963. ((char? c) 'char?)
  964. ((boolean? c) 'boolean?)
  965. ((keyword? c)
  966. (cond ((assq c markers) => cdr)
  967. (else 'keyword?)))
  968. ((vector? c) 'vector?)
  969. ((float-vector? c) 'float-vector?)
  970. ((int-vector? c) 'int-vector?)
  971. ((byte-vector? c) 'byte-vector?)
  972. ((let? c) 'let?)
  973. ((hash-table? c) 'hash-table?)
  974. ((input-port? c) 'input-port?)
  975. ((output-port? c) 'output-port?)
  976. ((iterator? c) 'iterator?)
  977. ((continuation? c) 'continuation?)
  978. ((dilambda? c) 'dilambda?)
  979. ((procedure? c) 'procedure?)
  980. ((macro? c) 'macro?)
  981. ((random-state? c) 'random-state?)
  982. ((c-pointer? c) 'c-pointer?)
  983. ((c-object? c) 'c-object?)
  984. ((eof-object? c) 'eof-object?)
  985. ((syntax? c) 'syntax?)
  986. ((assq c '((#<unspecified> . unspecified?) (#<undefined> . undefined?))) => cdr)
  987. (#t #t)))))
  988. (define (define->type c)
  989. (and (pair? c)
  990. (case (car c)
  991. ((define)
  992. (if (and (pair? (cdr c))
  993. (pair? (cadr c)))
  994. 'procedure?
  995. (and (pair? (cddr c))
  996. (->lint-type (caddr c)))))
  997. ((define* lambda lambda* case-lambda) 'procedure?)
  998. ((dilambda) 'dilambda?)
  999. ((define-macro define-macro* define-bacro define-bacro* defmacro defmacro* define-expansion) 'macro?)
  1000. ((:call/cc :call/exit) 'continuation?)
  1001. (else #t))))
  1002. (define (->lint-type c)
  1003. (cond ((not (pair? c)) (->simple-type c))
  1004. ((not (symbol? (car c))) (or (pair? (car c)) 'pair?))
  1005. ((not (eq? (car c) 'quote)) (or (return-type (car c) ()) (define->type c)))
  1006. ((symbol? (cadr c)) 'symbol?)
  1007. (else (->simple-type (cadr c))))) ; don't look for return type!
  1008. (define (compatible? type1 type2) ; we want type1, we have type2 -- is type2 ok?
  1009. (or (eq? type1 type2)
  1010. (not (symbol? type1))
  1011. (not (symbol? type2))
  1012. (not (hash-table-ref booleans type1))
  1013. (not (hash-table-ref booleans type2))
  1014. (eq? type2 'constant?)
  1015. (case type1
  1016. ((number? complex?) (memq type2 '(float? real? rational? integer? number? complex? exact? inexact? zero? negative? positive? even? odd? infinite? nan?)))
  1017. ((real?) (memq type2 '(float? rational? integer? complex? number? exact? inexact? zero? negative? positive? even? odd? infinite? nan?)))
  1018. ((zero?) (memq type2 '(float? real? rational? integer? number? complex? exact? inexact? even?)))
  1019. ((negative? positive?) (memq type2 '(float? real? rational? integer? complex? number? exact? inexact? even? odd? infinite? nan?)))
  1020. ((float?) (memq type2 '(real? complex? number? inexact? zero? negative? positive? infinite? nan?)))
  1021. ((rational?) (memq type2 '(integer? real? complex? number? exact? zero? negative? positive? even? odd?)))
  1022. ((integer?) (memq type2 '(real? rational? complex? number? exact? even? odd? zero? negative? positive?)))
  1023. ((odd? even?) (memq type2 '(real? rational? complex? number? exact? integer? zero? negative? positive?)))
  1024. ((exact?) (memq type2 '(real? rational? complex? number? integer? zero? negative? positive?)))
  1025. ((inexact?) (memq type2 '(real? number? complex? float? zero? negative? positive? infinite? nan?)))
  1026. ((infinite? nan?) (memq type2 '(real? number? complex? positive? negative? inexact? float?)))
  1027. ((vector?) (memq type2 '(float-vector? int-vector? sequence?)))
  1028. ((float-vector? int-vector?) (memq type2 '(vector? sequence?)))
  1029. ((sequence?) (memq type2 '(list? pair? null? proper-list? vector? float-vector? int-vector? byte-vector?
  1030. string? let? hash-table? c-object? iterator? procedure?))) ; procedure? for extended iterator
  1031. ((symbol?) (memq type2 '(gensym? keyword? defined? provided?)))
  1032. ((constant?) #t)
  1033. ((keyword? gensym? defined? provided?) (eq? type2 'symbol?))
  1034. ((list?) (memq type2 '(null? pair? proper-list? sequence?)))
  1035. ((proper-list?) (memq type2 '(null? pair? list? sequence?)))
  1036. ((pair? null?) (memq type2 '(list? proper-list? sequence?)))
  1037. ((dilambda?) (memq type2 '(procedure? macro? iterator?)))
  1038. ((procedure?) (memq type2 '(dilambda? iterator? macro? sequence?)))
  1039. ((macro?) (memq type2 '(dilambda? iterator? procedure?)))
  1040. ((iterator?) (memq type2 '(dilambda? procedure? sequence?)))
  1041. ((string?) (memq type2 '(byte-vector? sequence? directory? file-exists?)))
  1042. ((hash-table? let? c-object?)
  1043. (eq? type2 'sequence?))
  1044. ((byte-vector? directory? file-exists?)
  1045. (memq type2 '(string? sequence?)))
  1046. ((input-port? output-port?)
  1047. (eq? type2 'boolean?))
  1048. ((char? char-whitespace? char-numeric? char-alphabetic? char-upper-case? char-lower-case?)
  1049. (memq type2 '(char? char-whitespace? char-numeric? char-alphabetic? char-upper-case? char-lower-case?)))
  1050. (else #f))))
  1051. (define (any-compatible? type1 type2)
  1052. ;; type1 and type2 can be either a list of types or a type
  1053. (if (symbol? type1)
  1054. (if (symbol? type2)
  1055. (compatible? type1 type2)
  1056. (and (pair? type2)
  1057. (or (compatible? type1 (car type2))
  1058. (any-compatible? type1 (cdr type2)))))
  1059. (and (pair? type1)
  1060. (or (compatible? (car type1) type2)
  1061. (any-compatible? (cdr type1) type2)))))
  1062. (define (subsumes? type1 type2)
  1063. (or (eq? type1 type2)
  1064. (case type1
  1065. ((integer?) (memq type2 '(even? odd?)))
  1066. ((rational?) (memq type2 '(integer? exact? odd? even?)))
  1067. ((exact?) (memq type2 '(integer? rational?)))
  1068. ((real?) (memq type2 '(integer? rational? float? negative? positive? zero? odd? even?)))
  1069. ((complex? number?) (memq type2 '(integer? rational? float? real? complex? number? negative? positive? zero?
  1070. even? odd? exact? inexact? nan? infinite?)))
  1071. ((list?) (memq type2 '(pair? null? proper-list?)))
  1072. ((proper-list?) (eq? type2 'null?))
  1073. ((vector?) (memq type2 '(float-vector? int-vector?)))
  1074. ((symbol?) (memq type2 '(keyword? gensym? defined? provided?)))
  1075. ((sequence?) (memq type2 '(list? pair? null? proper-list? vector? float-vector? int-vector? byte-vector?
  1076. string? let? hash-table? c-object? directory? file-exists?)))
  1077. ((char?) (memq type2 '(char-whitespace? char-numeric? char-alphabetic? char-upper-case? char-lower-case?)))
  1078. (else #f))))
  1079. (define (never-false expr)
  1080. (or (eq? expr #t)
  1081. (let ((type (if (pair? expr)
  1082. (return-type (car expr) ())
  1083. (->lint-type expr))))
  1084. (and (symbol? type)
  1085. (not (symbol? expr))
  1086. (not (memq type '(boolean? values)))))))
  1087. (define (never-true expr)
  1088. (or (not expr)
  1089. (and (pair? expr)
  1090. (eq? (car expr) 'not)
  1091. (pair? (cdr expr))
  1092. (never-false (cadr expr)))))
  1093. (define (prettify-checker-unq op)
  1094. (if (pair? op)
  1095. (string-append (prettify-checker-unq (car op)) " or " (prettify-checker-unq (cadr op)))
  1096. (case op
  1097. ((rational?) "rational")
  1098. ((real?) "real")
  1099. ((complex?) "complex")
  1100. ((null?) "null")
  1101. ((length) "a sequence")
  1102. ((unspecified?) "untyped")
  1103. ((undefined?) "not defined")
  1104. (else
  1105. (let ((op-name (symbol->string op)))
  1106. (string-append (if (memv (op-name 0) '(#\a #\e #\i #\o #\u)) "an " "a ")
  1107. (substring op-name 0 (- (length op-name) 1))))))))
  1108. (define (prettify-checker op)
  1109. (if (pair? op)
  1110. (string-append (prettify-checker-unq (car op)) " or " (prettify-checker (cadr op)))
  1111. (let ((op-name (symbol->string op)))
  1112. (case op
  1113. ((rational? real? complex? null?) op-name)
  1114. ((unspecified?) "untyped")
  1115. ((undefined?) "not defined")
  1116. (else (string-append (if (memv (op-name 0) '(#\a #\e #\i #\o #\u)) "an " "a ") op-name))))))
  1117. (define (side-effect-with-vars? form env vars)
  1118. ;; (format *stderr* "~A~%" form)
  1119. ;; could evaluation of form have any side effects (like IO etc)
  1120. (if (or (not (proper-list? form)) ; we don't want dotted lists or () here
  1121. (null? form))
  1122. (and (symbol? form)
  1123. (or (eq? form '=>) ; (cond ((x => y))...) -- someday check y...
  1124. (let ((e (var-member form env)))
  1125. (if (var? e)
  1126. (and (symbol? (var-ftype e))
  1127. (var-side-effect e))
  1128. (and (not (hash-table-ref no-side-effect-functions form))
  1129. (procedure? (symbol->value form *e*))))))) ; i.e. function passed as argument
  1130. ;; can't optimize ((...)...) because the car might eval to a function
  1131. (or (and (not (hash-table-ref no-side-effect-functions (car form)))
  1132. ;; if it's not in the no-side-effect table and ...
  1133. (let ((e (var-member (car form) env)))
  1134. (or (not (var? e))
  1135. (not (symbol? (var-ftype e)))
  1136. (var-side-effect e)))
  1137. (or (not (eq? (car form) 'format)) ; (format #f ...)
  1138. (not (pair? (cdr form))) ; (format)!
  1139. (cadr form))
  1140. (or (null? vars)
  1141. (not (memq (car form) '(set!
  1142. ;vector-set! list-set! hash-table-set! float-vector-set! int-vector-set! string-set! let-set!
  1143. ;fill! string-fill! list-fill! vector-fill!
  1144. ;reverse! sort!
  1145. define define* define-macro define-macro* define-bacro define-bacro*)))))
  1146. ;; it's not the common (format #f ...) special case, then...(goto case below)
  1147. ;; else return #t: side-effects are possible -- this is too hard to read
  1148. (case (car form)
  1149. ((define-constant define-expansion) #t)
  1150. ((define define* define-macro define-macro* define-bacro define-bacro*)
  1151. (null? vars))
  1152. ((set!
  1153. ;vector-set! list-set! hash-table-set! float-vector-set! int-vector-set! string-set! let-set!
  1154. ;fill! string-fill! list-fill! vector-fill!
  1155. ;reverse! sort!
  1156. )
  1157. (or (not (pair? (cdr form)))
  1158. (not (symbol? (cadr form)))
  1159. (memq (cadr form) vars)))
  1160. ((quote) #f)
  1161. ((case)
  1162. (or (not (pair? (cdr form)))
  1163. (side-effect-with-vars? (cadr form) env vars) ; the selector
  1164. (let case-effect? ((f (cddr form)))
  1165. (and (pair? f)
  1166. (or (not (pair? (car f)))
  1167. (any? (lambda (ff) (side-effect-with-vars? ff env vars)) (cdar f))
  1168. (case-effect? (cdr f)))))))
  1169. ((cond)
  1170. (or (not (pair? (cadr form)))
  1171. (let cond-effect? ((f (cdr form))
  1172. (e env))
  1173. (and (pair? f)
  1174. (or (and (pair? (car f))
  1175. (any? (lambda (ff) (side-effect-with-vars? ff e vars)) (car f)))
  1176. (cond-effect? (cdr f) e))))))
  1177. ((let let* letrec letrec*)
  1178. ;; here if the var value involves a member of vars, we have to add it to vars
  1179. (or (< (length form) 3)
  1180. (let ((syms (cadr form))
  1181. (body (cddr form)))
  1182. (when (symbol? (cadr form))
  1183. (set! syms (caddr form))
  1184. (set! body (cdddr form)))
  1185. (if (and (pair? vars)
  1186. (pair? syms))
  1187. (for-each (lambda (sym)
  1188. (when (and (pair? sym)
  1189. (pair? (cdr sym))
  1190. (tree-set-member vars (cdr sym)))
  1191. (set! vars (cons (car sym) vars))))
  1192. syms))
  1193. (or (let let-effect? ((f syms) (e env) (v vars))
  1194. (and (pair? f)
  1195. (or (not (pair? (car f)))
  1196. (not (pair? (cdar f))) ; an error, reported elsewhere: (let ((x)) x)
  1197. (side-effect-with-vars? (cadar f) e v)
  1198. (let-effect? (cdr f) e v))))
  1199. (any? (lambda (ff) (side-effect-with-vars? ff env vars)) body)))))
  1200. ((do)
  1201. (or (< (length form) 3)
  1202. (not (list? (cadr form)))
  1203. (not (list? (caddr form)))
  1204. (let do-effect? ((f (cadr form)) (e env))
  1205. (and (pair? f)
  1206. (or (not (pair? (car f)))
  1207. (not (pair? (cdar f)))
  1208. (side-effect-with-vars? (cadar f) e vars)
  1209. (and (pair? (cddar f))
  1210. (side-effect-with-vars? (caddar f) e vars))
  1211. (do-effect? (cdr f) e))))
  1212. (any? (lambda (ff) (side-effect-with-vars? ff env vars)) (caddr form))
  1213. (any? (lambda (ff) (side-effect-with-vars? ff env vars)) (cdddr form))))
  1214. ;; ((lambda lambda*) (any? (lambda (ff) (side-effect-with-vars? ff env vars)) (cddr form))) ; this is trickier than it looks
  1215. (else
  1216. ;(format *stderr* "check args: ~A~%" form)
  1217. (or (any? (lambda (f) ; any subform has a side-effect
  1218. (and (not (null? f))
  1219. (side-effect-with-vars? f env vars)))
  1220. (cdr form))
  1221. (let ((sig (procedure-signature (car form)))) ; sig has func arg and it is not known safe
  1222. (and (pair? sig)
  1223. (memq 'procedure? (cdr sig))
  1224. (call-with-exit
  1225. (lambda (return)
  1226. (for-each
  1227. (lambda (sg arg)
  1228. (when (and (eq? sg 'procedure?)
  1229. (not (and (symbol? arg)
  1230. (hash-table-ref no-side-effect-functions arg))))
  1231. (return #t)))
  1232. (cdr sig) (cdr form))
  1233. #f))))))))))
  1234. (define (side-effect? form env)
  1235. (side-effect-with-vars? form env ()))
  1236. (define (just-constants? form env)
  1237. ;; can we probably evaluate form given just built-in stuff?
  1238. ;; watch out here -- this is used later by 'if, so (defined 'hiho) should not be evalled to #f!
  1239. (if (not (pair? form))
  1240. (constant? form)
  1241. (and (symbol? (car form))
  1242. (hash-table-ref no-side-effect-functions (car form))
  1243. (hash-table-ref built-in-functions (car form)) ; and not hook-functions
  1244. (not (var-member (car form) env)) ; e.g. exp declared locally as a list
  1245. (every? (lambda (p) (just-constants? p env)) (cdr form)))))
  1246. (define (equal-ignoring-constants? a b)
  1247. (or (morally-equal? a b)
  1248. (and (symbol? a)
  1249. (constant? a)
  1250. (morally-equal? (symbol->value a) b))
  1251. (and (symbol? b)
  1252. (constant? b)
  1253. (morally-equal? (symbol->value b) a))
  1254. (and (pair? a)
  1255. (pair? b)
  1256. (equal-ignoring-constants? (car a) (car b))
  1257. (equal-ignoring-constants? (cdr a) (cdr b)))))
  1258. (define (repeated-member? lst env)
  1259. (and (pair? lst)
  1260. (or (and (not (and (pair? (car lst))
  1261. (side-effect? (car lst) env)))
  1262. (pair? (cdr lst))
  1263. (member (car lst) (cdr lst)))
  1264. (repeated-member? (cdr lst) env))))
  1265. (define (update-scope v caller env)
  1266. (unless (or (memq caller (var-scope v))
  1267. (assq caller (var-scope v)))
  1268. (let ((cv (var-member caller env)))
  1269. (set! (var-scope v)
  1270. (cons (if (and (var? cv)
  1271. (memq (var-ftype cv) '(define lambda define* lambda*))) ; named-let does not define ftype
  1272. caller
  1273. (cons caller env))
  1274. (var-scope v))))))
  1275. (define (check-for-bad-variable-name caller vname)
  1276. (define (bad-variable-name-numbered vname bad-names)
  1277. (let ((str (symbol->string vname)))
  1278. (let loop ((bads bad-names))
  1279. (and (pair? bads)
  1280. (let* ((badstr (symbol->string (car bads)))
  1281. (pos (string-position badstr str)))
  1282. (or (and (eqv? pos 0)
  1283. (string->number (substring str (length badstr))))
  1284. (loop (cdr bads))))))))
  1285. (if (and (symbol? vname)
  1286. (pair? *report-bad-variable-names*)
  1287. (or (memq vname *report-bad-variable-names*)
  1288. (let ((sname (symbol->string vname)))
  1289. (and (> (length sname) 8)
  1290. (or (string=? "compute" (substring sname 0 7)) ; compute-* is as bad as get-*
  1291. (string=? "calculate" (substring sname 0 9))))) ; perhaps one exception: computed-goto*
  1292. (bad-variable-name-numbered vname *report-bad-variable-names*)))
  1293. (lint-format "surely there's a better name for this variable than ~A" caller vname)))
  1294. (define (set-ref name caller form env)
  1295. ;; if name is in env, set its "I've been referenced" flag
  1296. (let ((data (var-member name env)))
  1297. (if (var? data)
  1298. (begin
  1299. (set! (var-ref data) (+ (var-ref data) 1))
  1300. (update-scope data caller env)
  1301. (if (and form (not (memq form (var-history data))))
  1302. (set! (var-history data) (cons form (var-history data)))))
  1303. (if (not (defined? name (rootlet)))
  1304. (let ((old (hash-table-ref other-identifiers name)))
  1305. (check-for-bad-variable-name caller name)
  1306. (hash-table-set! other-identifiers name (if old (cons form old) (list form)))))))
  1307. env)
  1308. (define (set-set name caller form env)
  1309. (let ((data (var-member name env)))
  1310. (when (var? data)
  1311. (set! (var-set data) (+ (var-set data) 1))
  1312. (update-scope data caller env)
  1313. (if (not (memq caller (var-setters data)))
  1314. (set! (var-setters data) (cons caller (var-setters data))))
  1315. (if (not (memq form (var-history data)))
  1316. (set! (var-history data) (cons form (var-history data))))
  1317. (set! (var-signature data) #f)
  1318. (set! (var-ftype data) #f))))
  1319. (define (proper-list lst)
  1320. ;; return lst as a proper list
  1321. (if (not (pair? lst))
  1322. lst
  1323. (cons (car lst)
  1324. (if (pair? (cdr lst))
  1325. (proper-list (cdr lst))
  1326. (if (null? (cdr lst))
  1327. ()
  1328. (list (cdr lst)))))))
  1329. (define (keywords lst)
  1330. (do ((count 0)
  1331. (p lst (cdr p)))
  1332. ((null? p) count)
  1333. (if (keyword? (car p))
  1334. (set! count (+ count 1)))))
  1335. (define (eqv-selector clause)
  1336. (if (not (pair? clause))
  1337. (memq clause '(else #t))
  1338. (case (car clause)
  1339. ((memq memv member)
  1340. (and (= (length clause) 3)
  1341. (cadr clause)))
  1342. ((eq? eqv? = equal? char=? char-ci=? string=? string-ci=?)
  1343. (and (= (length clause) 3)
  1344. ((if (code-constant? (cadr clause)) caddr cadr) clause)))
  1345. ((or)
  1346. (and (pair? (cdr clause))
  1347. (eqv-selector (cadr clause))))
  1348. ((not null? eof-object? zero? boolean?)
  1349. (and (pair? (cdr clause))
  1350. (cadr clause)))
  1351. (else #f))))
  1352. (define (->eqf x)
  1353. (case x
  1354. ((char?) '(eqv? char=?))
  1355. ((integer? rational? real? number? complex?) '(eqv? =))
  1356. ((symbol? keyword? boolean? null? procedure? syntax? macro? undefined? unspecified?) '(eq? eq?))
  1357. ((string? byte-vector?) '(equal? string=?))
  1358. ((pair? vector? float-vector? int-vector? hash-table?) '(equal? equal?))
  1359. ((eof-object?) '(eq? eof-object?))
  1360. (else
  1361. (if (and (pair? x)
  1362. (pair? (cdr x))
  1363. (null? (cddr x))
  1364. (or (and (memq 'boolean? x)
  1365. (or (memq 'real? x) (memq 'number? x) (memq 'integer? x)))
  1366. (and (memq 'eof-object? x)
  1367. (or (memq 'char? x) (memq 'integer? x)))))
  1368. '(eqv? eqv?)
  1369. '(#t #t)))))
  1370. (define (eqf selector env)
  1371. (cond ((symbol? selector)
  1372. (if (and (not (var-member selector env))
  1373. (or (hash-table-ref built-in-functions selector)
  1374. (hash-table-ref syntaces selector)))
  1375. '(eq? eq?)
  1376. '(#t #t)))
  1377. ((not (pair? selector))
  1378. (->eqf (->lint-type selector)))
  1379. ((eq? (car selector) 'quote)
  1380. (cond ((or (symbol? (cadr selector))
  1381. (memq (cadr selector) '(#f #t #<unspecified> #<undefined> #<eof> ())))
  1382. '(eq? eq?))
  1383. ((char? (cadr selector)) '(eqv? char=?))
  1384. ((string? (cadr selector)) '(equal? string=?))
  1385. ((number? (cadr selector)) '(eqv? =))
  1386. (else '(equal? equal?))))
  1387. ((and (eq? (car selector) 'list)
  1388. (null? (cdr selector)))
  1389. '(eq? eq?))
  1390. ((symbol? (car selector))
  1391. (let ((sig (arg-signature (car selector) env)))
  1392. (if (pair? sig)
  1393. (->eqf (car sig))
  1394. '(#t #t))))
  1395. (else '(#t #t))))
  1396. (define (unquoted x)
  1397. (if (and (pair? x)
  1398. (eq? (car x) 'quote))
  1399. (cadr x)
  1400. x))
  1401. (define (distribute-quote x)
  1402. (map (lambda (item)
  1403. (if (or (symbol? item)
  1404. (pair? item))
  1405. `(quote ,item)
  1406. item))
  1407. x))
  1408. (define (focus-str str focus)
  1409. (let ((len (length str)))
  1410. (if (< len 40)
  1411. str
  1412. (let ((pos (string-position focus str))
  1413. (focus-len (length focus)))
  1414. (if (not pos)
  1415. str
  1416. (if (<= pos 20)
  1417. (string-append (substring str 0 (min 60 (- len 1) (+ focus-len pos 20))) " ...")
  1418. (string-append "... " (substring str (- pos 20) (min (- len 1) (+ focus-len pos 20))) " ...")))))))
  1419. (define (check-star-parameters f args env)
  1420. (if (list-any? (lambda (k) (memq k '(:key :optional))) args)
  1421. (let ((kw (if (memq :key args) :key :optional)))
  1422. (format outport "~NC~A: ~A is no longer accepted: ~A~%" lint-left-margin #\space f kw
  1423. (focus-str (object->string args) (symbol->string kw)))))
  1424. (if (member 'pi args (lambda (a b) (or (eq? b 'pi) (and (pair? b) (eq? (car b) 'pi)))))
  1425. (format outport "~NC~A: parameter can't be a constant: ~A~%" lint-left-margin #\space f
  1426. (focus-str (object->string args) "pi")))
  1427. (let ((r (memq :rest args)))
  1428. (when (pair? r)
  1429. (if (not (pair? (cdr r)))
  1430. (format outport "~NC~A: :rest parameter needs a name: ~A~%" lint-left-margin #\space f args)
  1431. (if (pair? (cadr r))
  1432. (format outport "~NC~A: :rest parameter can't specify a default value: ~A~%" lint-left-margin #\space f args)))))
  1433. (let ((a (memq :allow-other-keys args)))
  1434. (if (and (pair? a)
  1435. (pair? (cdr a)))
  1436. (format outport "~NC~A: :allow-other-keys should be at the end of the parameter list: ~A~%" lint-left-margin #\space f
  1437. (focus-str (object->string args) ":allow-other-keys"))))
  1438. (for-each (lambda (p)
  1439. (if (and (pair? p)
  1440. (pair? (cdr p)))
  1441. (lint-walk f (cadr p) env)))
  1442. args))
  1443. (define (checked-eval form)
  1444. (and (proper-list? form) ;(not (infinite? (length form))) but when would a dotted list work?
  1445. (catch #t
  1446. (lambda ()
  1447. (eval (copy form :readable)))
  1448. (lambda args
  1449. :checked-eval-error))))
  1450. (define (return-type-ok? type ret)
  1451. (or (eq? type ret)
  1452. (and (pair? ret)
  1453. (memq type ret))))
  1454. (define last-and-incomplete-arg2 #f)
  1455. (define (and-incomplete form head arg1 arg2 env) ; head: 'and | 'or (not ...) | 'if | 'if2 -- symbol arg1 in any case
  1456. (unless (memq (car arg2) '(and or not list cons vector)) ; these don't tell us anything about arg1's type
  1457. (let ((v (var-member arg1 env))) ; try to avoid the member->cdr trope
  1458. (unless (or (eq? arg2 last-and-incomplete-arg2)
  1459. (and (var? v)
  1460. (pair? (var-history v))
  1461. (member #f (var-history v)
  1462. (lambda (a b)
  1463. (and (pair? b)
  1464. (memq (car b) '(char-position string-position format string->number assoc assq assv memq memv member)))))))
  1465. (let* ((pos (do ((i 0 (+ i 1)) ; get arg number of arg1 in arg2
  1466. (p arg2 (cdr p))) ; 0th=car -> (and x (x))
  1467. ((or (null? p)
  1468. (eq? (car p) arg1))
  1469. i)))
  1470. (arg-type (let ((sig (and (positive? pos) ; procedure-signature for arg2
  1471. (arg-signature (car arg2) env))))
  1472. (if (zero? pos) ; it's type indication for arg1's position
  1473. 'procedure? ; or sequence? -- how to distinguish? use 'applicable?
  1474. (and (pair? sig)
  1475. (< pos (length sig))
  1476. (list-ref sig pos))))))
  1477. (let ((ln (and (< 0 line-number 100000) line-number))
  1478. (comment (if (and (eq? arg-type 'procedure?)
  1479. (= pos 0)
  1480. (pair? (cdr arg2)))
  1481. " ; or maybe sequence? " "")))
  1482. (set! last-and-incomplete-arg2 arg2) ; ignore unwanted repetitions due to recursive simplifications
  1483. (if (symbol? arg-type)
  1484. (let ((old-arg (case head
  1485. ((and if cond when) arg1)
  1486. ((or if2) `(not ,arg1))))
  1487. (new-arg (case head
  1488. ((and if cond when) `(,arg-type ,arg1))
  1489. ((or if2) `(not (,arg-type ,arg1))))))
  1490. (format outport "~NCin ~A~A,~%~NCperhaps change ~S to ~S~A~%"
  1491. lint-left-margin #\space
  1492. (truncated-list->string form)
  1493. (if ln (format #f " (line ~D)" ln) "")
  1494. (+ lint-left-margin 4) #\space
  1495. old-arg new-arg comment)))))))))
  1496. (define (and-redundant? arg1 arg2)
  1497. (let ((type1 (car arg1))
  1498. (type2 (car arg2)))
  1499. (and (symbol? type1)
  1500. (symbol? type2)
  1501. (hash-table-ref booleans type1)
  1502. (or (hash-table-ref booleans type2) ; return #f if not (obviously) redundant, else return which of the two to keep
  1503. (memq type2 '(= char=? string=? not eq?)))
  1504. (if (eq? type1 type2)
  1505. type1
  1506. (case type1
  1507. ((number? complex?)
  1508. (case type2
  1509. ((float? real? rational? integer?) type2)
  1510. ((number? complex?) type1)
  1511. ((=) (let ((x ((if (number? (caddr arg2)) caddr cadr) arg2)))
  1512. (and (number? x)
  1513. (if (= x (floor x)) 'memv 'eqv?))))
  1514. (else #f)))
  1515. ((real?)
  1516. (case type2
  1517. ((float? rational? integer?) type2)
  1518. ((number? complex?) type1)
  1519. ((=) (let ((x ((if (real? (caddr arg2)) caddr cadr) arg2)))
  1520. (and (real? x)
  1521. (if (= x (floor x)) 'memv 'eqv?))))
  1522. (else #f)))
  1523. ((float?)
  1524. (and (memq type2 '(real? complex? number? inexact?)) type1))
  1525. ((rational?)
  1526. (case type2
  1527. ((integer?) type2)
  1528. ((real? complex? number? exact?) type1)
  1529. ((=)
  1530. (and (or (rational? (caddr arg2))
  1531. (rational? (cadr arg2)))
  1532. 'eqv?))
  1533. (else #f)))
  1534. ((integer?)
  1535. (case type2
  1536. ((real? rational? complex? number? exact?) type1)
  1537. ((=)
  1538. (and (or (integer? (caddr arg2))
  1539. (integer? (cadr arg2)))
  1540. 'eqv?))
  1541. (else #f)))
  1542. ((exact?)
  1543. (and (memq type2 '(rational? integer?)) type2))
  1544. ((even? odd?)
  1545. (and (memq type2 '(integer? rational? real? complex? number?)) type1)) ; not zero? -> 0.0
  1546. ((zero?)
  1547. (and (memq type2 '(complex? number? real?)) type1))
  1548. ((negative? positive?)
  1549. (and (eq? type2 'real?) type1))
  1550. ((inexact?)
  1551. (and (eq? type2 'float?) type2))
  1552. ((infinite? nan?)
  1553. (and (memq type2 '(number? complex? inexact?)) type1))
  1554. ((vector?)
  1555. (and (memq type2 '(float-vector? int-vector?)) type2))
  1556. ((float-vector? int-vector?)
  1557. (and (eq? type2 'vector?) type1))
  1558. ((symbol?)
  1559. (case type2
  1560. ((keyword? gensym?) type2)
  1561. ((eq?)
  1562. (and (or (quoted-symbol? (cadr arg2))
  1563. (quoted-symbol? (caddr arg2)))
  1564. 'eq?))
  1565. (else #f)))
  1566. ((keyword?)
  1567. (case type2
  1568. ((symbol? constant?) type1)
  1569. ((eq?)
  1570. (and (or (keyword? (cadr arg2))
  1571. (keyword? (caddr arg2)))
  1572. 'eq?))
  1573. (else #f)))
  1574. ((gensym? defined? provided?)
  1575. (and (eq? type2 'symbol?) type1))
  1576. ((boolean?)
  1577. (and (or (eq? type2 'not)
  1578. (and (eq? type2 'eq?)
  1579. (or (boolean? (cadr arg2))
  1580. (boolean? (caddr arg2)))))
  1581. type2))
  1582. ((list?)
  1583. (and (memq type2 '(null? pair? proper-list?)) type2))
  1584. ((null?)
  1585. (and (memq type2 '(list? proper-list?)) type1))
  1586. ((pair?)
  1587. (and (eq? type2 'list?) type1))
  1588. ((proper-list?)
  1589. (and (eq? type2 'null?) type2))
  1590. ((string?)
  1591. (case type2
  1592. ((byte-vector?) type2)
  1593. ((string=?)
  1594. (and (or (eq? (->lint-type (cadr arg2)) 'string?)
  1595. (eq? (->lint-type (caddr arg2)) 'string?))
  1596. 'equal?))
  1597. (else #f)))
  1598. ((char?)
  1599. (and (eq? type2 'char=?)
  1600. (or (eq? (->lint-type (cadr arg2)) 'char?)
  1601. (eq? (->lint-type (caddr arg2)) 'char?))
  1602. 'eqv?))
  1603. ((char-numeric? char-whitespace? char-alphabetic? char-upper-case? char-lower-case?)
  1604. (and (eq? type2 'char?) type1))
  1605. ((byte-vector? directory? file-exists?)
  1606. (and (eq? type2 'string?) type1))
  1607. (else #f))))))
  1608. (define (and-forgetful form head arg1 arg2 env)
  1609. (unless (or (memq (car arg2) '(and or not list cons vector)) ; these don't tell us anything about arg1's type
  1610. (eq? arg2 last-and-incomplete-arg2))
  1611. (let* ((pos (do ((i 0 (+ i 1)) ; get arg number of arg1 in arg2
  1612. (p arg2 (cdr p))) ; 0th=car -> (and x (x))
  1613. ((or (null? p)
  1614. (equal? (car p) (cadr arg1)))
  1615. (if (null? p) -1 i))))
  1616. (arg-type (let ((sig (and (positive? pos) ; procedure-signature for arg2
  1617. (arg-signature (car arg2) env))))
  1618. (if (zero? pos) ; its type indication for arg1's position
  1619. 'procedure? ; or sequence? -- how to distinguish? use 'applicable?
  1620. (and (pair? sig)
  1621. (< pos (length sig))
  1622. (list-ref sig pos))))))
  1623. (when (symbol? arg-type)
  1624. (let ((new-type (and-redundant? arg1 (cons arg-type (cdr arg1)))))
  1625. (when (and new-type
  1626. (not (eq? new-type (car arg1))))
  1627. (let ((old-arg (case head
  1628. ((and if cond when) arg1)
  1629. ((or if2) `(not ,arg1))))
  1630. (new-arg (case head
  1631. ((and if cond when) `(,new-type ,(cadr arg1)))
  1632. ((or if2) `(not (,new-type ,(cadr arg1))))))
  1633. (ln (and (< 0 line-number 100000) line-number))
  1634. (comment (if (and (eq? arg-type 'procedure?)
  1635. (= pos 0)
  1636. (pair? (cdr arg2)))
  1637. " ; or maybe sequence? " "")))
  1638. (set! last-and-incomplete-arg2 arg2)
  1639. (format outport "~NCin ~A~A,~%~NCperhaps change ~S to ~S~A~%"
  1640. lint-left-margin #\space
  1641. (truncated-list->string form)
  1642. (if ln (format #f " (line ~D)" ln) "")
  1643. (+ lint-left-margin 4) #\space
  1644. old-arg new-arg comment)))))))
  1645. ;; perhaps change pair? -> eq? or ignore it?
  1646. (when (and (pair? (cdr arg2))
  1647. (not (eq? (car arg1) 'pair?)))
  1648. (let ((a2 (if (eq? (car arg2) 'not)
  1649. (cadr arg2)
  1650. arg2)))
  1651. (when (and (pair? a2)
  1652. (memq (car a2) '(memq memv member assq assv assoc eq? eqv? equal?))
  1653. (equal? (cadr arg1) (cadr a2)))
  1654. (let ((new-e (case (car (->eqf (car arg1)))
  1655. ((eq?)
  1656. (case (car a2)
  1657. ((memq assq eq?) (car a2))
  1658. ((memv member) 'memq)
  1659. ((assv assoc) 'assq)
  1660. ((eqv? equal?) 'eq?)))
  1661. ((eqv?)
  1662. (case (car a2)
  1663. ((memv assv eqv?) (car a2))
  1664. ((memq member) 'memv)
  1665. ((assq assoc) 'assv)
  1666. ((eq? equal?) 'eqv?)))
  1667. ((equal?)
  1668. (case (car a2)
  1669. ((member assoc equal?) (car a2))
  1670. ((memq memv) 'member)
  1671. ((assq assv) 'assoc)
  1672. ((eq? eqv?) 'equal?)))
  1673. (else (car a2)))))
  1674. (when (and (not (eq? (car a2) new-e))
  1675. (symbol? new-e))
  1676. (let ((ln (and (< 0 line-number 100000) line-number)))
  1677. (format outport "~NCin ~A~A,~%~NCperhaps change ~A to ~A~%"
  1678. lint-left-margin #\space
  1679. (truncated-list->string form)
  1680. (if ln (format #f " (line ~D)" ln) "")
  1681. (+ lint-left-margin 4) #\space
  1682. (truncated-list->string a2)
  1683. `(,new-e ...)))))))))
  1684. ;; --------------------------------
  1685. (define simplify-boolean
  1686. (let ((notables (let ((h (make-hash-table)))
  1687. (for-each
  1688. (lambda (op)
  1689. (set! (h (car op)) (cadr op)))
  1690. '((< >=) (> <=) (<= >) (>= <)
  1691. (char<? char>=?) (char>? char<=?) (char<=? char>?) (char>=? char<?)
  1692. (string<? string>=?) (string>? string<=?) (string<=? string>?) (string>=? string<?)
  1693. (char-ci<? char-ci>=?) (char-ci>? char-ci<=?) (char-ci<=? char-ci>?) (char-ci>=? char-ci<?)
  1694. (string-ci<? string-ci>=?) (string-ci>? string-ci<=?) (string-ci<=? string-ci>?) (string-ci>=? string-ci<?)
  1695. (odd? even?) (even? odd?) (exact? inexact?) (inexact? exact?)))
  1696. h))
  1697. (relsub
  1698. (let ((relops '((< <= > number?) (<= < >= number?) (> >= < number?) (>= > <= number?)
  1699. (char<? char<=? char>? char?) (char<=? char<? char>=? char?) ; these never happen
  1700. (char>? char>=? char<? char?) (char>=? char>? char<=? char?)
  1701. (string<? string<=? string>? string?) (string<=? string<? string>=? string?)
  1702. (string>? string>=? string<? string?) (string>=? string>? string<=? string?))))
  1703. (lambda (A B rel-op env)
  1704. (call-with-exit
  1705. (lambda (return)
  1706. (when (and (pair? A)
  1707. (pair? B)
  1708. (= (length A) (length B) 3))
  1709. (let ((Adata (assq (car A) relops))
  1710. (Bdata (assq (car B) relops)))
  1711. (when (and Adata Bdata)
  1712. (let ((op1 (car A))
  1713. (op2 (car B))
  1714. (A1 (cadr A))
  1715. (A2 (caddr A))
  1716. (B1 (cadr B))
  1717. (B2 (caddr B)))
  1718. (let ((x (if (and (not (number? A1))
  1719. (member A1 B))
  1720. A1
  1721. (and (not (number? A2))
  1722. (member A2 B)
  1723. A2))))
  1724. (when x
  1725. (let ((c1 (if (equal? x A1) A2 A1))
  1726. (c2 (if (equal? x B1) B2 B1))
  1727. (type (cadddr Adata)))
  1728. (if (or (side-effect? c1 env)
  1729. (side-effect? c2 env)
  1730. (side-effect? x env))
  1731. (return 'ok))
  1732. (if (equal? x A2) (set! op1 (caddr Adata)))
  1733. (if (equal? x B2) (set! op2 (caddr Bdata)))
  1734. (let ((typer #f)
  1735. (gtes #f)
  1736. (gts #f)
  1737. (eqop #f))
  1738. (case type
  1739. ((number?)
  1740. (set! typer number?)
  1741. (set! gtes '(>= <=))
  1742. (set! gts '(< >))
  1743. (set! eqop '=))
  1744. ((char?)
  1745. (set! typer char?)
  1746. (set! gtes '(char>=? char<=?))
  1747. (set! gts '(char<? char>?))
  1748. (set! eqop 'char=?))
  1749. ((string?)
  1750. (set! typer string?)
  1751. (set! gtes '(string>=? string<=?))
  1752. (set! gts '(string<? string>?))
  1753. (set! eqop 'string=?)))
  1754. (case rel-op
  1755. ((and)
  1756. (cond ((equal? c1 c2)
  1757. (cond ((eq? op1 op2)
  1758. (return `(,op1 ,x ,c1)))
  1759. ((eq? op2 (cadr (assq op1 relops)))
  1760. (return `(,(if (memq op2 gtes) op1 op2) ,x ,c1)))
  1761. ((and (memq op1 gtes)
  1762. (memq op2 gtes))
  1763. (return `(,eqop ,x ,c1)))
  1764. (else (return #f))))
  1765. ((and (typer c1)
  1766. (typer c2))
  1767. (cond ((or (eq? op1 op2)
  1768. (eq? op2 (cadr (assq op1 relops))))
  1769. (return (if ((symbol->value op1) c1 c2)
  1770. `(,op1 ,x ,c1)
  1771. `(,op2 ,x ,c2))))
  1772. ((eq? op1 (caddr (assq op2 relops)))
  1773. (if ((symbol->value op1) c2 c1)
  1774. (return `(,op1 ,c2 ,x ,c1))
  1775. (if (memq op1 gts)
  1776. (return #f))))
  1777. ((and (eq? op2 (hash-table-ref reversibles (cadr (assq op1 relops))))
  1778. ((symbol->value op1) c1 c2))
  1779. (return #f))))
  1780. ((eq? op2 (caddr (assq op1 relops)))
  1781. (return `(,op1 ,c2 ,x ,c1)))))
  1782. ((or)
  1783. (cond ((equal? c1 c2)
  1784. (cond ((eq? op1 op2)
  1785. (return `(,op1 ,x ,c1)))
  1786. ((eq? op2 (cadr (assq op1 relops)))
  1787. (return `(,(if (memq op2 gtes) op2 op1) ,x ,c1)))
  1788. ((and (memq op1 gts)
  1789. (memq op2 gts))
  1790. (return `(not (,eqop ,x ,c1))))
  1791. (else (return #t))))
  1792. ((and (typer c1)
  1793. (typer c2))
  1794. (cond ((or (eq? op1 op2)
  1795. (eq? op2 (cadr (assq op1 relops))))
  1796. (return (if ((symbol->value op1) c1 c2)
  1797. `(,op2 ,x ,c2)
  1798. `(,op1 ,x ,c1))))
  1799. ((eq? op1 (caddr (assq op2 relops)))
  1800. (if ((symbol->value op1) c2 c1)
  1801. (return #t))
  1802. (return `(not (,(cadr (assq op1 relops)) ,c1 ,x ,c2))))
  1803. ((and (eq? op2 (hash-table-ref reversibles (cadr (assq op1 relops))))
  1804. ((symbol->value op1) c2 c1))
  1805. (return #t))))
  1806. ((eq? op2 (caddr (assq op1 relops)))
  1807. (return `(not (,(cadr (assq op1 relops)) ,c1 ,x ,c2)))))))))))))))
  1808. 'ok))))))
  1809. (lambda (in-form true false env)
  1810. (define (classify e)
  1811. (if (not (just-constants? e env))
  1812. e
  1813. (catch #t
  1814. (lambda ()
  1815. (let ((val (eval e)))
  1816. (if (boolean? val)
  1817. val
  1818. e)))
  1819. (lambda ignore e))))
  1820. (define (contradictory? ands)
  1821. (let ((vars ()))
  1822. (call-with-exit
  1823. (lambda (return)
  1824. (do ((b ands (cdr b)))
  1825. ((null? b) #f)
  1826. (if (and (pair? b)
  1827. (pair? (car b))
  1828. (pair? (cdar b)))
  1829. (let ((func (caar b))
  1830. (args (cdar b)))
  1831. (if (memq func '(eq? eqv? equal?))
  1832. (if (and (symbol? (car args))
  1833. (code-constant? (cadr args)))
  1834. (set! func (->lint-type (cadr args)))
  1835. (if (and (symbol? (cadr args))
  1836. (code-constant? (car args)))
  1837. (set! func (->lint-type (car args))))))
  1838. (if (symbol? func)
  1839. (for-each
  1840. (lambda (arg)
  1841. (if (symbol? arg)
  1842. (let ((type (assq arg vars)))
  1843. (if (not type)
  1844. (set! vars (cons (cons arg func) vars))
  1845. (if (not (compatible? (cdr type) func))
  1846. (return #t))))))
  1847. args)))))))))
  1848. (define (and-redundants env . args)
  1849. (do ((locals ())
  1850. (diffs #f)
  1851. (p args (cdr p)))
  1852. ((or (null? p)
  1853. (not (and (pair? (car p))
  1854. (pair? (cdar p))
  1855. (hash-table-ref booleans (caar p)))))
  1856. (and (null? p)
  1857. (pair? locals)
  1858. (or diffs
  1859. (any? (lambda (a) (pair? (cddr a))) locals))
  1860. (let ((keepers ()))
  1861. (for-each (lambda (a)
  1862. (let ((next-a (cdr a)))
  1863. (cond ((null? (cdr next-a))
  1864. (set! keepers (cons (car next-a) keepers)))
  1865. ((null? (cddr next-a))
  1866. (let ((res (apply and-redundant? (reverse next-a))))
  1867. (if res
  1868. (begin
  1869. (set! keepers (cons ((if (eq? res (caar next-a)) car cadr) next-a) keepers))
  1870. (set! diffs #t))
  1871. (set! keepers (cons (car next-a) (cons (cadr next-a) keepers))))))
  1872. (else
  1873. (let ((ar (reverse next-a)))
  1874. (let ((ar1 (car ar))
  1875. (ar2 (cadr ar))
  1876. (ar3 (caddr ar)))
  1877. (let ((res1 (and-redundant? ar1 ar2)) ; if res1 either 1 or 2 is out
  1878. (res2 (and-redundant? ar2 ar3)) ; if res2 either 2 or 3 is out
  1879. (res3 (and-redundant? ar1 ar3))) ; if res3 either 1 or 3 is out
  1880. ;; only in numbers can 3 actually be reducible
  1881. (if (not (or res1 res2 res3))
  1882. (set! keepers (append (cdr a) keepers))
  1883. (begin
  1884. (set! diffs #t)
  1885. (if (and (or (not res1)
  1886. (eq? res1 (car ar1)))
  1887. (or (not res3)
  1888. (eq? res3 (car ar1))))
  1889. (set! keepers (cons ar1 keepers)))
  1890. (if (and (or (not res1)
  1891. (eq? res1 (car ar2)))
  1892. (or (not res2)
  1893. (eq? res2 (car ar2))))
  1894. (set! keepers (cons ar2 keepers)))
  1895. (if (and (or (not res2)
  1896. (eq? res2 (car ar3)))
  1897. (or (not res3)
  1898. (eq? res3 (car ar3))))
  1899. (set! keepers (cons ar3 keepers)))
  1900. (if (pair? (cdddr ar))
  1901. (set! keepers (append (reverse (cdddr ar)) keepers))))))))))))
  1902. (reverse locals))
  1903. (and diffs (reverse keepers)))))
  1904. (let* ((bool (car p))
  1905. (local (assoc (cadr bool) locals)))
  1906. (if (pair? local)
  1907. (if (member bool (cdr local))
  1908. (set! diffs #t)
  1909. (set-cdr! local (cons bool (cdr local))))
  1910. (set! locals (cons (list (cadr bool) bool) locals))))))
  1911. (define (and-not-redundant arg1 arg2)
  1912. (let ((type1 (car arg1)) ; (? ...)
  1913. (type2 (caadr arg2))) ; (not (? ...))
  1914. (and (symbol? type1)
  1915. (symbol? type2)
  1916. (or (hash-table-ref booleans type1)
  1917. (memq type1 '(= char=? string=?)))
  1918. (hash-table-ref booleans type2)
  1919. (if (eq? type1 type2) ; (and (?) (not (?))) -> #f
  1920. 'contradictory
  1921. (case type1
  1922. ((pair?)
  1923. (case type2
  1924. ((list?) 'contradictory)
  1925. ((proper-list?) #f)
  1926. (else arg1)))
  1927. ((null?)
  1928. (if (eq? type2 'list?)
  1929. 'contradictory
  1930. arg1))
  1931. ((list?)
  1932. (case type2
  1933. ((pair?) 'null?)
  1934. ((null?) 'pair?)
  1935. ((proper-list?) #f)
  1936. (else arg1)))
  1937. ((proper-list?)
  1938. (case type2
  1939. ((list? pair?) 'contradictory)
  1940. ((null?) #f)
  1941. (else arg1)))
  1942. ((symbol?)
  1943. (and (not (memq type2 '(keyword? gensym?)))
  1944. arg1))
  1945. ((char=?)
  1946. (if (eq? type2 'char?)
  1947. 'contradictory
  1948. (and (or (char? (cadr arg1))
  1949. (char? (caddr arg1)))
  1950. `(eqv? ,@(cdr arg1))))) ; arg2 might be (not (eof-object?...))
  1951. ((real?)
  1952. (case type2
  1953. ((rational? exact?) `(float? ,@(cdr arg1)))
  1954. ((inexact?) `(rational? ,@(cdr arg1)))
  1955. ((complex? number?) 'contradictory)
  1956. ((negative? positive? even? odd? zero? integer?) #f)
  1957. (else arg1)))
  1958. ((integer?)
  1959. (case type2
  1960. ((real? complex? number? rational? exact?) 'contradictory)
  1961. ((float? inexact? infinite? nan?) arg1)
  1962. (else #f)))
  1963. ((rational?)
  1964. (case type2
  1965. ((real? complex? number? exact?) 'contradictory)
  1966. ((float? inexact? infinite? nan?) arg1)
  1967. (else #f)))
  1968. ((complex? number?)
  1969. (and (memq type2 '(complex? number?))
  1970. 'contradictory))
  1971. ((float?)
  1972. (case type2
  1973. ((real? complex? number? inexact?) 'contradictory)
  1974. ((rational? integer? exact?) arg1)
  1975. (else #f)))
  1976. ((exact?)
  1977. (case type2
  1978. ((rational?) 'contradictory)
  1979. ((inexact? infinite? nan?) arg1)
  1980. (else #f)))
  1981. ((even? odd?)
  1982. (case type2
  1983. ((integer? exact? rational? real? number? complex?) 'contradictory)
  1984. ((infinite? nan?) arg1)
  1985. (else #f)))
  1986. ((zero? negative? positive?)
  1987. (and (memq type2 '(complex? number? real?))
  1988. 'contradictory))
  1989. ((infinite? nan?)
  1990. (case type2
  1991. ((number? complex? inexact?) 'contradictory)
  1992. ((integer? rational? exact? even? odd?) arg1)
  1993. (else #f)))
  1994. ((char-whitespace? char-numeric? char-alphabetic? char-upper-case? char-lower-case?)
  1995. (and (eq? type2 'char?)
  1996. 'contradictory))
  1997. ((directory? file-exists?)
  1998. (and (memq type2 '(string? sequence?))
  1999. 'contradictory))
  2000. (else
  2001. ;; none of the rest happen
  2002. #f))))))
  2003. (define (or-not-redundant arg1 arg2)
  2004. (let ((type1 (car arg1)) ; (? ...)
  2005. (type2 (caadr arg2))) ; (not (? ...))
  2006. (and (symbol? type1)
  2007. (symbol? type2)
  2008. (or (hash-table-ref bools type1)
  2009. (memq type1 '(= char=? string=?)))
  2010. (hash-table-ref bools type2)
  2011. (if (eq? type1 type2) ; (or (?) (not (?))) -> #t
  2012. 'fatuous
  2013. (case type1
  2014. ((null?)
  2015. (case type2
  2016. ((list?) ; not proper-list? here
  2017. `(not (pair? ,(cadr arg1))))
  2018. ((proper-list?) #f)
  2019. (else arg2)))
  2020. ((eof-object?)
  2021. arg2) ; not keyword? here because (or (not (symbol? x)) (keyword? x)) is not reducible to (not (symbol? x))
  2022. ((string?)
  2023. (and (not (eq? type2 'byte-vector?)) arg2))
  2024. (else #f))))))
  2025. (define (bsimp x) ; quick check for common easy cases
  2026. (set! last-simplify-boolean-line-number line-number)
  2027. (if (not (and (pair? x)
  2028. (pair? (cdr x))))
  2029. x
  2030. (case (car x)
  2031. ((and) (and (cadr x) ; (and #f ...) -> #f
  2032. x))
  2033. ((or) (if (and (cadr x) ; (or #t ...) -> #t
  2034. (code-constant? (cadr x)))
  2035. (cadr x)
  2036. x))
  2037. (else
  2038. (if (not (and (= (length x) 2)
  2039. (pair? (cadr x))
  2040. (symbol? (caadr x))))
  2041. x
  2042. (let ((rt (if (eq? (caadr x) 'quote)
  2043. (->simple-type (cadadr x))
  2044. (return-type (caadr x) env)))
  2045. (head (car x)))
  2046. (or (and (subsumes? head rt) #t) ; don't return the memq list!
  2047. (and (or (memq rt '(#t #f values))
  2048. (any-compatible? head rt))
  2049. (case head
  2050. ((null?) (if (eq? (caadr x) 'list)
  2051. (null? (cdadr x))
  2052. x))
  2053. ((pair?) (if (eq? (caadr x) 'list)
  2054. (pair? (cdadr x))
  2055. x))
  2056. ((negative?) (and (not (hash-table-ref non-negative-ops (caadr x)))
  2057. x))
  2058. (else x))))))))))
  2059. (define (bcomp x) ; not so quick...
  2060. (cond ((not (pair? x))
  2061. x)
  2062. ((eq? (car x) 'and)
  2063. (call-with-exit
  2064. (lambda (return)
  2065. (let ((newx (list 'and)))
  2066. (do ((p (cdr x) (cdr p))
  2067. (sidex newx)
  2068. (endx newx))
  2069. ((null? p) newx)
  2070. (let ((next (car p)))
  2071. (if (or (not next) ; #f in and -> end of expr
  2072. (member next false))
  2073. (if (eq? sidex newx) ; no side-effects
  2074. (return #f)
  2075. (begin
  2076. (set-cdr! endx (list #f))
  2077. (return newx)))
  2078. (if (or (code-constant? next) ; (and ... true-expr ...)
  2079. (member next sidex) ; if a member, and no side-effects since, it must be true
  2080. (member next true))
  2081. (if (and (null? (cdr p))
  2082. (not (equal? next (car endx))))
  2083. (set-cdr! endx (list next)))
  2084. (begin
  2085. (set-cdr! endx (list next))
  2086. (set! endx (cdr endx))
  2087. (if (side-effect? next env)
  2088. (set! sidex endx)))))))))))
  2089. ((not (eq? (car x) 'or))
  2090. x)
  2091. (else
  2092. (call-with-exit
  2093. (lambda (return)
  2094. (let ((newx (list 'or)))
  2095. (do ((p (cdr x) (cdr p))
  2096. (sidex newx)
  2097. (endx newx))
  2098. ((null? p) newx)
  2099. (let ((next (car p)))
  2100. (if (or (and next ; (or ... #t ...)
  2101. (code-constant? next))
  2102. (member next true))
  2103. (begin
  2104. (set-cdr! endx (list next))
  2105. (return newx)) ; we're done since this is true
  2106. (if (or (not next)
  2107. (member next sidex) ; so its false in some way
  2108. (member next false))
  2109. (if (and (null? (cdr p))
  2110. (not (equal? next (car endx))))
  2111. (set-cdr! endx (list next)))
  2112. (begin
  2113. (set-cdr! endx (list next))
  2114. (set! endx (cdr endx))
  2115. (if (side-effect? next env)
  2116. (set! sidex endx)))))))))))))
  2117. (define (gather-or-eqf-elements eqfnc sym vals)
  2118. (let* ((func (case eqfnc
  2119. ((eq?) 'memq)
  2120. ((eqv? char=?) 'memv)
  2121. (else 'member)))
  2122. (equals (if (and (eq? func 'member)
  2123. (not (eq? eqfnc 'equal?)))
  2124. (list eqfnc)
  2125. ()))
  2126. (elements (lint-remove-duplicates (map unquoted vals) env)))
  2127. (cond ((null? (cdr elements))
  2128. `(,eqfnc ,sym ,@elements))
  2129. ((and (eq? eqfnc 'char=?)
  2130. (= (length elements) 2)
  2131. (char-ci=? (car elements) (cadr elements)))
  2132. `(char-ci=? ,sym ,(car elements)))
  2133. ((and (eq? eqfnc 'string=?)
  2134. (= (length elements) 2)
  2135. (string-ci=? (car elements) (cadr elements)))
  2136. `(string-ci=? ,sym ,(car elements)))
  2137. ((member elements '((#t #f) (#f #t)))
  2138. `(boolean? ,sym)) ; zero? doesn't happen
  2139. (else
  2140. `(,func ,sym ',(reverse elements) ,@equals)))))
  2141. (define (reversible-member expr lst)
  2142. (and (pair? lst)
  2143. (or (member expr lst)
  2144. (and (eqv? (length expr) 3)
  2145. (let ((rev-op (hash-table-ref reversibles (car expr))))
  2146. (and rev-op
  2147. (member (list rev-op (caddr expr) (cadr expr)) lst)))))))
  2148. (define and-rel-ops (let ((h (make-hash-table)))
  2149. (for-each (lambda (op)
  2150. (hash-table-set! h op #t))
  2151. '(< = > <= >= char-ci>=? char-ci<? char-ready? char<? char-ci=? char>?
  2152. char<=? char-ci>? char-ci<=? char>=? char=? string-ci<=? string=?
  2153. string-ci>=? string<? string-ci<? string-ci=? string-ci>? string>=? string<=? string>?
  2154. eqv? equal? eq? morally-equal?))
  2155. h))
  2156. ;; --------------------------------
  2157. ;; start of simplify-boolean code
  2158. ;; this is not really simplify boolean as in boolean algebra because in scheme there are many unequal truths, but only one falsehood
  2159. ;; 'and and 'or are not boolean operators in a sense
  2160. ;; (format *stderr* "simplify: ~A~%" in-form)
  2161. (and (not (or (reversible-member in-form false)
  2162. (and (pair? in-form)
  2163. (eq? (car in-form) 'not)
  2164. (pair? (cdr in-form)) ; (not)!
  2165. (reversible-member (cadr in-form) true))))
  2166. (or (and (reversible-member in-form true) #t)
  2167. (and (pair? in-form)
  2168. (eq? (car in-form) 'not)
  2169. (pair? (cdr in-form))
  2170. (reversible-member (cadr in-form) false)
  2171. #t)
  2172. (if (not (pair? in-form))
  2173. in-form
  2174. (let ((form (bcomp (bsimp in-form))))
  2175. (if (not (and (pair? form)
  2176. (memq (car form) '(or and not))))
  2177. (classify form)
  2178. (let ((len (length form)))
  2179. (let ((op (case (car form)
  2180. ((or) 'and)
  2181. ((and) 'or)
  2182. (else #f))))
  2183. (if (and op
  2184. (>= len 3)
  2185. (every? (lambda (p)
  2186. (and (pair? p)
  2187. (pair? (cdr p))
  2188. (pair? (cddr p))
  2189. (eq? (car p) op)))
  2190. (cdr form)))
  2191. (let ((first (cadadr form)))
  2192. (if (every? (lambda (p)
  2193. (equal? (cadr p) first))
  2194. (cddr form))
  2195. (set! form `(,op ,first (,(car form) ,@(map (lambda (p)
  2196. (if (null? (cdddr p))
  2197. (caddr p)
  2198. `(,op ,@(cddr p))))
  2199. (cdr form)))))
  2200. (if (null? (cdddr (cadr form)))
  2201. (let ((last (caddr (cadr form))))
  2202. (if (every? (lambda (p)
  2203. (and (null? (cdddr p))
  2204. (equal? (caddr p) last)))
  2205. (cddr form))
  2206. (set! form `(,op (,(car form) ,@(map cadr (cdr form))) ,last)))))))))
  2207. ;; (or (and A B) (and A C)) -> (and A (or B C))
  2208. ;; (or (and A B) (and C B)) -> (and (or A C) B)
  2209. ;; (and (or A B) (or A C)) -> (or A (and B C))
  2210. ;; (and (or A B) (or C B)) -> (or (and A C) B)
  2211. (case (car form)
  2212. ;; --------------------------------
  2213. ((not)
  2214. (if (not (= len 2))
  2215. form
  2216. (let* ((arg (cadr form))
  2217. (val (classify (if (and (pair? arg)
  2218. (memq (car arg) '(and or not)))
  2219. (simplify-boolean arg true false env)
  2220. arg)))
  2221. (arg-op (and (pair? arg)
  2222. (car arg))))
  2223. (cond ((boolean? val)
  2224. (not val))
  2225. ((or (code-constant? arg)
  2226. (and (pair? arg)
  2227. (symbol? arg-op)
  2228. (hash-table-ref no-side-effect-functions arg-op)
  2229. (let ((ret (return-type arg-op env)))
  2230. (and (or (symbol? ret) (pair? ret))
  2231. (not (return-type-ok? 'boolean? ret))))
  2232. (not (var-member arg-op env))))
  2233. #f)
  2234. ((and (pair? val) ; (not (not ...)) -> ...
  2235. (pair? (cdr val)) ; this is usually internally generated,
  2236. (memq (car val) '(not if cond case begin))) ; so the message about (and x #t) is in special-case-functions below
  2237. (case (car val)
  2238. ((not)
  2239. (cadr val))
  2240. ((if)
  2241. (let ((if-true (simplify-boolean `(not ,(caddr val)) () () env))
  2242. (if-false (or (not (pair? (cdddr val))) ; (not #<unspecified>) -> #t
  2243. (simplify-boolean `(not ,(cadddr val)) () () env))))
  2244. ;; ideally we'd call if-walker on this to simplify further
  2245. `(if ,(cadr val) ,if-true ,if-false)))
  2246. ((cond case)
  2247. `(,(car val)
  2248. ,@(if (eq? (car val) 'cond) () (list (cadr val)))
  2249. ,@(map (lambda (c)
  2250. (if (not (and (pair? c)
  2251. (pair? (cdr c))))
  2252. c
  2253. (let* ((len (length (cdr c)))
  2254. (new-last (let ((last (list-ref c len)))
  2255. (if (and (pair? last)
  2256. (eq? (car last) 'error))
  2257. last
  2258. (simplify-boolean `(not ,last) () () env)))))
  2259. `(,(car c) ,@(copy (cdr c) (make-list (- len 1))) ,new-last))))
  2260. ((if (eq? (car val) 'cond) cdr cddr) val))))
  2261. ((begin)
  2262. (let* ((len1 (- (length val) 1))
  2263. (new-last (simplify-boolean `(not ,(list-ref val len1)) () () env)))
  2264. `(,@(copy val (make-list len1)) ,new-last)))))
  2265. ((not (equal? val arg))
  2266. `(not ,val))
  2267. ((not (pair? arg))
  2268. form)
  2269. ((and (memq arg-op '(and or)) ; (not (or|and x (not y))) -> (and|or (not x) y)
  2270. (= (length arg) 3)
  2271. (or (and (pair? (cadr arg))
  2272. (eq? (caadr arg) 'not))
  2273. (and (pair? (caddr arg))
  2274. (eq? (caaddr arg) 'not))))
  2275. (let ((rel (if (eq? arg-op 'or) 'and 'or)))
  2276. `(,rel ,@(map (lambda (p)
  2277. (if (and (pair? p)
  2278. (eq? (car p) 'not))
  2279. (cadr p)
  2280. (simplify-boolean `(not ,p) () () env)))
  2281. (cdr arg)))))
  2282. ((<= (length arg) 3) ; avoid (<= 0 i 12) and such
  2283. (case arg-op
  2284. ((< > <= >= odd? even? exact? inexact?char<? char>? char<=? char>=? string<? string>? string<=? string>=?
  2285. char-ci<? char-ci>? char-ci<=? char-ci>=? string-ci<? string-ci>? string-ci<=? string-ci>=?)
  2286. `(,(hash-table-ref notables arg-op) ,@(cdr arg)))
  2287. ;; null? is not quite right because (not (null? 3)) -> #t
  2288. ;; char-upper-case? and lower are not switchable here
  2289. ((zero?) ; (not (zero? (logand p 2^n | (ash 1 i)))) -> (logbit? p i)
  2290. (let ((zarg (cadr arg))) ; (logand...)
  2291. (if (not (and (pair? zarg)
  2292. (eq? (car zarg) 'logand)
  2293. (pair? (cdr zarg))
  2294. (pair? (cddr zarg))
  2295. (null? (cdddr zarg))))
  2296. form
  2297. (let ((arg1 (cadr zarg))
  2298. (arg2 (caddr zarg))) ; these are never reversed
  2299. (or (and (pair? arg2)
  2300. (pair? (cdr arg2))
  2301. (eq? (car arg2) 'ash)
  2302. (eqv? (cadr arg2) 1)
  2303. `(logbit? ,arg1 ,(caddr arg2)))
  2304. (and (integer? arg2)
  2305. (positive? arg2)
  2306. (zero? (logand arg2 (- arg2 1))) ; it's a power of 2
  2307. `(logbit? ,arg1 ,(floor (log arg2 2)))) ; floor for freeBSD?
  2308. form)))))
  2309. (else form)))
  2310. (else form)))))
  2311. ;; --------------------------------
  2312. ((or)
  2313. (case len
  2314. ((1) #f)
  2315. ((2) (if (code-constant? (cadr form)) (cadr form) (classify (cadr form))))
  2316. (else
  2317. (call-with-exit
  2318. (lambda (return)
  2319. (when (= len 3)
  2320. (let ((arg1 (cadr form))
  2321. (arg2 (caddr form)))
  2322. (if (and (pair? arg2) ; (or A (and ... A ...)) -> A
  2323. (eq? (car arg2) 'and)
  2324. (member arg1 (cdr arg2))
  2325. (not (side-effect? arg2 env)))
  2326. (return arg1))
  2327. (if (and (pair? arg1) ; (or (and ... A) A) -> A
  2328. (eq? (car arg1) 'and)
  2329. (equal? arg2 (list-ref arg1 (- (length arg1) 1)))
  2330. (not (side-effect? arg1 env)))
  2331. (return arg2))
  2332. (when (pair? arg2)
  2333. (if (and (eq? (car arg2) 'and) ; (or A (and (not A) B)) -> (or A B)
  2334. (pair? (cadr arg2))
  2335. (eq? (caadr arg2) 'not)
  2336. (equal? arg1 (cadadr arg2)))
  2337. (return `(or ,arg1 ,@(cddr arg2))))
  2338. (when (pair? arg1)
  2339. (when (eq? (car arg1) 'not)
  2340. (if (symbol? (cadr arg1))
  2341. (if (memq (cadr arg1) arg2)
  2342. (begin
  2343. (if (eq? (car arg2) 'boolean?)
  2344. (return arg2))
  2345. (and-incomplete form 'or (cadr arg1) arg2 env))
  2346. (do ((p arg2 (cdr p)))
  2347. ((or (not (pair? p))
  2348. (and (pair? (car p))
  2349. (memq (cadr arg1) (car p))))
  2350. (if (pair? p)
  2351. (and-incomplete form 'or (cadr arg1) (car p) env)))))
  2352. (if (and (pair? (cadr arg1)) ; (or (not (number? x)) (> x 2)) -> (or (not (real? x)) (> x 2))
  2353. (hash-table-ref bools (caadr arg1)))
  2354. (if (member (cadadr arg1) arg2)
  2355. (and-forgetful form 'or (cadr arg1) arg2 env)
  2356. (do ((p arg2 (cdr p)))
  2357. ((or (not (pair? p))
  2358. (and (pair? (car p))
  2359. (member (cadadr arg1) (car p))))
  2360. (if (pair? p)
  2361. (and-forgetful form 'or (cadr arg1) (car p) env)))))))
  2362. (if (and (eq? (car arg2) 'and) ; (or (not A) (and A B)) -> (or (not A) B) -- this stuff actually happens!
  2363. (equal? (cadr arg1) (cadr arg2)))
  2364. (return `(or ,arg1 ,@(cddr arg2)))))
  2365. (when (and (eq? (car arg1) 'and)
  2366. (eq? (car arg2) 'and)
  2367. (= 3 (length arg1) (length arg2))
  2368. ;; (not (side-effect? arg1 env)) ; maybe??
  2369. (or (equal? (cadr arg1) `(not ,(cadr arg2)))
  2370. (equal? `(not ,(cadr arg1)) (cadr arg2)))
  2371. (not (equal? (caddr arg1) `(not ,(caddr arg2))))
  2372. (not (equal? `(not ,(caddr arg1)) (caddr arg2))))
  2373. ;; kinda dumb, but common: (or (and A B) (and (not A) C)) -> (if A B C)
  2374. ;; the other side: (and (or A B) (or (not A) C)) -> (if A C (and B #t)), but it never happens
  2375. (lint-format "perhaps ~A" 'or
  2376. (lists->string form
  2377. (if (and (pair? (cadr arg1))
  2378. (eq? (caadr arg1) 'not))
  2379. `(if ,(cadr arg2) ,(caddr arg2) ,(caddr arg1))
  2380. `(if ,(cadr arg1) ,(caddr arg1) ,(caddr arg2))))))
  2381. (let ((t1 (and (pair? (cdr arg1))
  2382. (pair? (cdr arg2))
  2383. (or (equal? (cadr arg1) (cadr arg2))
  2384. (and (pair? (cddr arg2))
  2385. (null? (cdddr arg2))
  2386. (equal? (cadr arg1) (caddr arg2))))
  2387. (not (side-effect? arg1 env))
  2388. (and-redundant? arg1 arg2))))
  2389. (if t1
  2390. (return (if (eq? t1 (car arg1)) arg2 arg1))))
  2391. ;; if all clauses are (eq-func x y) where one of x/y is a symbol|simple-expr repeated throughout
  2392. ;; and the y is a code-constant, or -> memq and friends.
  2393. ;; This could also handle cadr|caddr reversed, but it apparently never happens.
  2394. (if (and (or (and (eq? (car arg2) '=)
  2395. (memq (car arg1) '(< > <= >=)))
  2396. (and (eq? (car arg1) '=)
  2397. (memq (car arg2) '(< > <= >=))))
  2398. (= (length arg1) 3)
  2399. (equal? (cdr arg1) (cdr arg2)))
  2400. (return `(,(if (or (memq (car arg1) '(< <=))
  2401. (memq (car arg2) '(< <=)))
  2402. '<= '>=)
  2403. ,@(cdr arg1))))
  2404. ;; this makes some of the code above redundant
  2405. (let ((rel (relsub arg1 arg2 'or env)))
  2406. (if (or (boolean? rel)
  2407. (pair? rel))
  2408. (return rel)))
  2409. ;; (or (pair? x) (null? x)) -> (list? x)
  2410. (when (and (pair? (cdr arg1))
  2411. (pair? (cdr arg2))
  2412. (equal? (cadr arg1) (cadr arg2)))
  2413. (if (and (memq (car arg1) '(null? pair?))
  2414. (memq (car arg2) '(null? pair?))
  2415. (not (eq? (car arg1) (car arg2))))
  2416. (return `(list? ,(cadr arg1))))
  2417. (if (and (eq? (car arg1) 'zero?) ; (or (zero? x) (positive? x)) -> (not (negative? x)) -- other cases don't happen
  2418. (memq (car arg2) '(positive? negative?)))
  2419. (return `(not (,(if (eq? (car arg2) 'positive?) 'negative? 'positive?) ,(cadr arg1))))))
  2420. ;; (or (and A B) (and (not A) (not B))) -> (eq? (not A) (not B))
  2421. ;; more accurately (if A B (not B)), but every case I've seen is just boolean
  2422. ;; perhaps also (or (not (or A B)) (not (or (not A) (not B)))), but it never happens
  2423. (let ((a1 (cadr form))
  2424. (a2 (caddr form)))
  2425. (when (and (pair? a1)
  2426. (pair? a2)
  2427. (eq? (car a1) 'and)
  2428. (eq? (car a2) 'and)
  2429. (= (length a1) 3)
  2430. (= (length a2) 3))
  2431. (let ((A ((if (and (pair? (cadr a1)) (eq? (caadr a1) 'not)) cadadr cadr) a1))
  2432. (B (if (and (pair? (caddr a1)) (eq? (caaddr a1) 'not)) (cadr (caddr a1)) (caddr a1))))
  2433. (if (or (equal? form `(or (and ,A ,B) (and (not ,A) (not ,B))))
  2434. (equal? form `(or (and (not ,A) (not ,B)) (and ,A ,B))))
  2435. (return `(eq? (not ,A) (not ,B))))
  2436. (if (or (equal? form `(or (and ,A (not ,B)) (and (not ,A) ,B)))
  2437. (equal? form `(or (and (not ,A) ,B) (and ,A (not ,B)))))
  2438. (return `(not (eq? (not ,A) (not ,B))))))))
  2439. (when (and (pair? (cdr arg1))
  2440. (pair? (cdr arg2))
  2441. (not (eq? (car arg1) (car arg2))))
  2442. (when (subsumes? (car arg1) (car arg2))
  2443. (return arg1))
  2444. (if (eq? (car arg1) 'not)
  2445. (let ((temp arg1))
  2446. (set! arg1 arg2)
  2447. (set! arg2 temp)))
  2448. (when (and (eq? (car arg2) 'not)
  2449. (pair? (cadr arg2))
  2450. (pair? (cdadr arg2))
  2451. (not (eq? (caadr arg2) 'let?))
  2452. (or (equal? (cadr arg1) (cadadr arg2))
  2453. (and (pair? (cddr arg1))
  2454. (equal? (caddr arg1) (cadadr arg2))))
  2455. (eq? (return-type (car arg1) env) 'boolean?)
  2456. (eq? (return-type (caadr arg2) env) 'boolean?))
  2457. (let ((t2 (or-not-redundant arg1 arg2)))
  2458. (when t2
  2459. (if (eq? t2 'fatuous)
  2460. (return #t)
  2461. (if (pair? t2)
  2462. (return t2)))))))
  2463. ;; (or (if a c d) (if b c d)) -> (if (or a b) c d) never happens, sad to say
  2464. ;; or + if + if does happen but not in this easily optimized form
  2465. )))) ; len = 3
  2466. ;; len > 3 or nothing was caught above
  2467. (let ((nots 0)
  2468. (revers 0)
  2469. (arglen (- len 1)))
  2470. (for-each (lambda (a)
  2471. (if (pair? a)
  2472. (if (eq? (car a) 'not)
  2473. (set! nots (+ nots 1))
  2474. (if (hash-table-ref notables (car a))
  2475. (set! revers (+ revers 1))))))
  2476. (cdr form))
  2477. (if (= nots arglen) ; every arg is `(not ...)
  2478. (let ((nf (simplify-boolean `(and ,@(map cadr (cdr form))) () () env)))
  2479. (return (simplify-boolean `(not ,nf) () () env)))
  2480. (if (and (> arglen 2)
  2481. (or (> nots (/ (* 2 arglen) 3))
  2482. (and (> arglen 2)
  2483. (> nots (/ arglen 2))
  2484. (> revers 0))))
  2485. (let ((nf (simplify-boolean `(and ,@(map (lambda (p)
  2486. (cond ((not (pair? p))
  2487. `(not ,p))
  2488. ((eq? (car p) 'not)
  2489. (cadr p))
  2490. ((hash-table-ref notables (car p)) =>
  2491. (lambda (op)
  2492. `(,op ,@(cdr p))))
  2493. (else `(not ,p))))
  2494. (cdr form)))
  2495. () () env)))
  2496. (return (simplify-boolean `(not ,nf) () () env))))))
  2497. (let ((sym #f)
  2498. (eqfnc #f)
  2499. (vals ())
  2500. (start #f))
  2501. (define (constant-arg p)
  2502. (if (code-constant? (cadr p))
  2503. (set! vals (cons (cadr p) vals))
  2504. (and (code-constant? (caddr p))
  2505. (set! vals (cons (caddr p) vals)))))
  2506. (define (upgrade-eqf)
  2507. (set! eqfnc (if (memq eqfnc '(string=? string-ci=? = equal?))
  2508. 'equal?
  2509. (if (memq eqfnc '(#f eq?)) 'eq? 'eqv?))))
  2510. (do ((fp (cdr form) (cdr fp)))
  2511. ((null? fp))
  2512. (let ((p (and (pair? fp)
  2513. (car fp))))
  2514. (if (and (pair? p)
  2515. (if (not sym)
  2516. (set! sym (eqv-selector p))
  2517. (equal? sym (eqv-selector p)))
  2518. (or (not (memq eqfnc '(char-ci=? string-ci=? =)))
  2519. (memq (car p) '(char-ci=? string-ci=? =)))
  2520. ;; = can't share: (equal? 1 1.0) -> #f, so (or (not x) (= x 1)) can't be simplified
  2521. ;; except via member+morally-equal? but that brings in float-epsilon and NaN differences.
  2522. ;; We could add both: 1 1.0 as in cond?
  2523. ;;
  2524. ;; another problem: using memx below means the returned value of the expression
  2525. ;; may not match the original (#t -> '(...)), so perhaps we should add a one-time
  2526. ;; warning about this, and wrap it in (pair? (mem...)) as an example.
  2527. ;;
  2528. ;; and another thing... the original might be broken: (eq? x #(1)) where equal?
  2529. ;; is more sensible, but that also changes the behavior of the expression:
  2530. ;; (memq x '(#(1))) may be #f (or #t!) when (member x '(#(1))) is '(#(1)).
  2531. ;;
  2532. ;; I think I'll try to turn out a more-or-less working expression, but warn about it.
  2533. (case (car p)
  2534. ((string=? equal?)
  2535. (set! eqfnc (if (or (not eqfnc)
  2536. (eq? eqfnc (car p)))
  2537. (car p)
  2538. 'equal?))
  2539. (and (= (length p) 3)
  2540. (constant-arg p)))
  2541. ((char=?)
  2542. (if (memq eqfnc '(#f char=?))
  2543. (set! eqfnc 'char=?)
  2544. (if (not (eq? eqfnc 'equal?))
  2545. (set! eqfnc 'eqv?)))
  2546. (and (= (length p) 3)
  2547. (constant-arg p)))
  2548. ((eq? eqv?)
  2549. (let ((leqf (car (->eqf (->lint-type ((if (code-constant? (cadr p)) cadr caddr) p))))))
  2550. (cond ((not eqfnc)
  2551. (set! eqfnc leqf))
  2552. ((or (memq leqf '(#t equal?))
  2553. (not (eq? eqfnc leqf)))
  2554. (set! eqfnc 'equal?))
  2555. ((memq eqfnc '(#f eq?))
  2556. (set! eqfnc leqf))))
  2557. (and (= (length p) 3)
  2558. (constant-arg p)))
  2559. ((char-ci=? string-ci=? =)
  2560. (and (or (not eqfnc)
  2561. (eq? eqfnc (car p)))
  2562. (set! eqfnc (car p))
  2563. (= (length p) 3)
  2564. (constant-arg p)))
  2565. ((eof-object?)
  2566. (upgrade-eqf)
  2567. (set! vals (cons #<eof> vals)))
  2568. ((not)
  2569. (upgrade-eqf)
  2570. (set! vals (cons #f vals)))
  2571. ((boolean?)
  2572. (upgrade-eqf)
  2573. (set! vals (cons #f (cons #t vals))))
  2574. ((zero?)
  2575. (if (memq eqfnc '(#f eq?)) (set! eqfnc 'eqv?))
  2576. (set! vals (cons 0 (cons 0.0 vals))))
  2577. ((null?)
  2578. (upgrade-eqf)
  2579. (set! vals (cons () vals)))
  2580. ((memq memv member)
  2581. (cond ((eq? (car p) 'member)
  2582. (set! eqfnc 'equal?))
  2583. ((eq? (car p) 'memv)
  2584. (set! eqfnc (if (eq? eqfnc 'string=?) 'equal? 'eqv?)))
  2585. ((not eqfnc)
  2586. (set! eqfnc 'eq?)))
  2587. (and (= (length p) 3)
  2588. (pair? (caddr p))
  2589. (eq? 'quote (caaddr p))
  2590. (pair? (cadr (caddr p)))
  2591. (set! vals (append (cadr (caddr p)) vals))))
  2592. (else #f)))
  2593. (if (not start)
  2594. (set! start fp)
  2595. (if (null? (cdr fp))
  2596. (return (if (eq? start (cdr form))
  2597. (gather-or-eqf-elements eqfnc sym vals)
  2598. `(or ,@(copy (cdr form) (make-list (let loop ((g (cdr form)) (len 0))
  2599. (if (eq? g start)
  2600. len
  2601. (loop (cdr g) (+ len 1))))))
  2602. ,(gather-or-eqf-elements eqfnc sym vals))))))
  2603. (when start
  2604. (if (eq? fp (cdr start))
  2605. (begin
  2606. (set! sym #f)
  2607. (set! eqfnc #f)
  2608. (set! vals ())
  2609. (set! start #f))
  2610. ;; here we have possible header stuff + more than one match + trailing stuff
  2611. (let ((trailer (if (not (and (pair? fp)
  2612. (pair? (cdr fp))))
  2613. fp
  2614. (let ((nfp (simplify-boolean `(or ,@fp) () () env)))
  2615. ((if (and (pair? nfp)
  2616. (eq? (car nfp) 'or))
  2617. cdr list)
  2618. nfp)))))
  2619. (return (if (eq? start (cdr form))
  2620. `(or ,(gather-or-eqf-elements eqfnc sym vals)
  2621. ,@trailer)
  2622. `(or ,@(copy (cdr form) (make-list (let loop ((g (cdr form)) (len 0))
  2623. (if (eq? g start)
  2624. len
  2625. (loop (cdr g) (+ len 1))))))
  2626. ,(gather-or-eqf-elements eqfnc sym vals)
  2627. ,@trailer)))))))))
  2628. (do ((selector #f) ; (or (and (eq?...)...)....) -> (case ....)
  2629. (keys ())
  2630. (fp (cdr form) (cdr fp)))
  2631. ((or (null? fp)
  2632. (let ((p (and (pair? fp)
  2633. (car fp))))
  2634. (not (and (pair? p)
  2635. (eq? (car p) 'and)
  2636. (pair? (cdr p))
  2637. (pair? (cadr p))
  2638. (pair? (cdadr p))
  2639. (or selector
  2640. (set! selector (cadadr p)))
  2641. (let ((expr (cadr p))
  2642. (arg1 (cadadr p)))
  2643. (case (car expr)
  2644. ((null?)
  2645. (and (equal? selector arg1)
  2646. (not (memq () keys))
  2647. (set! keys (cons () keys))))
  2648. ;; we have to make sure no keys are repeated:
  2649. ;; (or (and (eq? x 'a) (< y 1)) (and (eq? x 'a) (< y 2)))
  2650. ;; this rewrite has become much trickier than expected...
  2651. ((boolean?)
  2652. (and (equal? selector arg1)
  2653. (not (memq #f keys))
  2654. (not (memq #t keys))
  2655. (set! keys (cons #f (cons #t keys)))))
  2656. ((eof-object?)
  2657. (and (equal? selector arg1)
  2658. (not (memq #<eof> keys))
  2659. (set! keys (cons #<eof> keys))))
  2660. ((zero?)
  2661. (and (equal? selector arg1)
  2662. (not (memv 0 keys))
  2663. (not (memv 0.0 keys))
  2664. (set! keys (cons 0.0 (cons 0 keys)))))
  2665. ((memq memv)
  2666. (and (equal? selector arg1)
  2667. (pair? (cddr expr))
  2668. (pair? (caddr expr))
  2669. (eq? (caaddr expr) 'quote)
  2670. (pair? (cadr (caddr expr)))
  2671. (not (any? (lambda (g)
  2672. (memv g keys))
  2673. (cadr (caddr expr))))
  2674. (set! keys (append (cadr (caddr expr)) keys))))
  2675. ((eq? eqv? char=?)
  2676. (and (pair? (cddr expr))
  2677. (null? (cdddr expr))
  2678. (or (and (equal? selector arg1)
  2679. (code-constant? (caddr expr))
  2680. (not (memv (unquoted (caddr expr)) keys))
  2681. (set! keys (cons (unquoted (caddr expr)) keys)))
  2682. (and (equal? selector (caddr expr))
  2683. (code-constant? arg1)
  2684. (not (memv (unquoted arg1) keys))
  2685. (set! keys (cons (unquoted arg1) keys))))))
  2686. ((not)
  2687. ;; no hits here for last+not eq(etc)+no collision in keys
  2688. (and (equal? selector arg1)
  2689. (not (memq #f keys))
  2690. (set! keys (cons #f keys))))
  2691. (else #f)))))))
  2692. (if (null? fp)
  2693. (return `(case ,selector
  2694. ,@(map (lambda (p)
  2695. (let ((result (if (null? (cdddr p))
  2696. (caddr p)
  2697. `(and ,@(cddr p))))
  2698. (key (let ((expr (cadr p)))
  2699. (case (car expr)
  2700. ((eq? eqv? char=?)
  2701. (let ((repeats (equal? selector (cadr expr))))
  2702. (list (unquoted ((if repeats caddr cadr) expr)))))
  2703. ((memq memv) (unquoted (caddr expr)))
  2704. ((null?) (list ()))
  2705. ((eof-object?) (list #<eof>))
  2706. ((zero?) (list 0 0.0))
  2707. ((not) (list #f))
  2708. ((boolean?) (list #t #f))))))
  2709. (list key result)))
  2710. (cdr form))
  2711. (else #f))))))
  2712. (do ((new-form ())
  2713. (retry #f)
  2714. (exprs (cdr form) (cdr exprs)))
  2715. ((null? exprs)
  2716. (return (and (pair? new-form)
  2717. (if (null? (cdr new-form))
  2718. (car new-form)
  2719. (if retry
  2720. (simplify-boolean `(or ,@(reverse new-form)) () () env)
  2721. `(or ,@(reverse new-form)))))))
  2722. (let ((val (classify (car exprs)))
  2723. (old-form new-form))
  2724. (when (and (pair? val)
  2725. (memq (car val) '(and or not)))
  2726. (set! val (classify (simplify-boolean val true false env)))
  2727. (when (and (> len 3)
  2728. (pair? val)
  2729. (eq? (car val) 'not)
  2730. (pair? (cdr exprs)))
  2731. (if (symbol? (cadr val))
  2732. (if (and (pair? (cadr exprs))
  2733. (memq (cadr val) (cadr exprs)))
  2734. (and-incomplete form 'or (cadr val) (cadr exprs) env)
  2735. (do ((ip (cdr exprs) (cdr ip))
  2736. (found-it #f))
  2737. ((or found-it
  2738. (not (pair? ip))))
  2739. (do ((p (car ip) (cdr p)))
  2740. ((or (not (pair? p))
  2741. (and (memq (cadr val) p)
  2742. (set! found-it p)))
  2743. (if (pair? found-it)
  2744. (and-incomplete form 'or (cadr val) found-it env))))))
  2745. (when (and (pair? (cadr val))
  2746. (pair? (cadr exprs))
  2747. (hash-table-ref bools (caadr val)))
  2748. (if (member (cadadr val) (cadr exprs))
  2749. (and-forgetful form 'or (cadr val) (cadr exprs) env)
  2750. (do ((p (cadr exprs) (cdr p)))
  2751. ((or (not (pair? p))
  2752. (and (pair? (car p))
  2753. (member (cadadr val) (car p))))
  2754. (if (pair? p)
  2755. (and-forgetful form 'or (cadr val) (car p) env)))))))))
  2756. (if (not (or retry
  2757. (equal? val (car exprs))))
  2758. (set! retry #t))
  2759. (cond ((not val)) ; #f in or is ignored
  2760. ((or (eq? val #t) ; #t or any non-#f constant in or ends the expression
  2761. (code-constant? val))
  2762. (set! new-form (if (null? new-form) ; (or x1 123) -> value of x1 first
  2763. (list val)
  2764. (cons val new-form)))
  2765. ;; reversed when returned
  2766. (set! exprs '(#t)))
  2767. ((and (pair? val) ; (or ...) -> splice into current
  2768. (eq? (car val) 'or))
  2769. (set! exprs (append val (cdr exprs)))) ; we'll skip the 'or in do step
  2770. ((not (or (memq val new-form)
  2771. (and (pair? val) ; and redundant tests
  2772. (hash-table-ref booleans (car val))
  2773. (any? (lambda (p)
  2774. (and (pair? p)
  2775. (subsumes? (car p) (car val))
  2776. (equal? (cadr val) (cadr p))))
  2777. new-form))))
  2778. (set! new-form (cons val new-form))))
  2779. (if (and (not (eq? new-form old-form))
  2780. (pair? (cdr new-form)))
  2781. (let ((rel (relsub (cadr new-form) (car new-form) 'or env))) ; new-form is reversed
  2782. (if (or (boolean? rel)
  2783. (pair? rel))
  2784. (set! new-form (cons rel (cddr new-form))))))))))))))
  2785. ;; --------------------------------
  2786. ((and)
  2787. (case len
  2788. ((1) #t)
  2789. ((2) (classify (cadr form)))
  2790. (else
  2791. (and (not (contradictory? (cdr form)))
  2792. (call-with-exit
  2793. (lambda (return)
  2794. (when (= len 3)
  2795. (let ((arg1 (cadr form))
  2796. (arg2 (caddr form)))
  2797. (if (and (pair? arg2) ; (and A (or A ...)) -> A
  2798. (eq? (car arg2) 'or)
  2799. (equal? arg1 (cadr arg2))
  2800. (not (side-effect? arg2 env)))
  2801. (return arg1))
  2802. (if (and (pair? arg1) ; (and (or ... A ...) A) -> A
  2803. (eq? (car arg1) 'or)
  2804. (member arg2 (cdr arg1))
  2805. (not (side-effect? arg1 env)))
  2806. (return arg2))
  2807. ;; the and equivalent of (or (not A) (and A B)) never happens
  2808. (when (pair? arg2)
  2809. (if (symbol? arg1) ; (and x (pair? x)) -> (pair? x)
  2810. (if (memq arg1 arg2)
  2811. (begin
  2812. (case (car arg2)
  2813. ((not) (return #f))
  2814. ((boolean?) (return `(eq? ,arg1 #t))))
  2815. (and-incomplete form 'and arg1 arg2 env)
  2816. (if (hash-table-ref booleans (car arg2))
  2817. (return arg2)))
  2818. (do ((p arg2 (cdr p))) ; (and x (+ (log x) 1)) -> (and (number? x)...)
  2819. ((or (not (pair? p))
  2820. (and (pair? (car p))
  2821. (memq arg1 (car p))))
  2822. (if (pair? p)
  2823. (and-incomplete form 'and arg1 (car p) env)))))
  2824. (if (and (pair? arg1) ; (and (number? x) (> x 2)) -> (and (real? x) (> x 2))
  2825. (hash-table-ref bools (car arg1)))
  2826. (if (member (cadr arg1) arg2)
  2827. (and-forgetful form 'and arg1 arg2 env)
  2828. (do ((p arg2 (cdr p)))
  2829. ((or (not (pair? p))
  2830. (and (pair? (car p))
  2831. (member (cadr arg1) (car p))))
  2832. (if (pair? p)
  2833. (and-forgetful form 'and arg1 (car p) env))))))))
  2834. (if (and (not (side-effect? arg1 env))
  2835. (equal? arg1 arg2)) ; (and x x) -> x
  2836. (return arg1))
  2837. (when (and (pair? arg1)
  2838. (pair? arg2)
  2839. (pair? (cdr arg1))
  2840. (pair? (cdr arg2)))
  2841. (let ((t1 (and (or (equal? (cadr arg1) (cadr arg2))
  2842. (and (pair? (cddr arg2))
  2843. (null? (cdddr arg2))
  2844. (equal? (cadr arg1) (caddr arg2))))
  2845. (not (side-effect? arg1 env))
  2846. (and-redundant? arg1 arg2)))) ; (and (integer? x) (number? x)) -> (integer? x)
  2847. (if t1
  2848. (return (cond
  2849. ((memq t1 '(eq? eqv? equal?))
  2850. `(,t1 ,@(cdr arg2)))
  2851. ((eq? t1 'memv)
  2852. (let ((x ((if (equal? (cadr arg1) (cadr arg2)) caddr cadr) arg2)))
  2853. (if (rational? x)
  2854. `(memv ,(cadr arg1) '(,x ,(* 1.0 x)))
  2855. `(memv ,(cadr arg1) '(,(floor x) ,x)))))
  2856. ((eq? t1 (car arg1)) arg1)
  2857. (else arg2)))))
  2858. (when (and (hash-table-ref reversibles (car arg1))
  2859. (pair? (cddr arg1))
  2860. (null? (cdddr arg1))
  2861. (pair? (cddr arg2))
  2862. (null? (cdddr arg2))
  2863. (not (side-effect? arg2 env)) ; arg1 is hit in any case
  2864. (or (eq? (car arg1) (car arg2)) ; either ops are equal or
  2865. (let ((rf (hash-table-ref reversibles (car arg2)))) ; try reversed op for arg2
  2866. (and (eq? (car arg1) rf)
  2867. (set! arg2 (cons rf (reverse (cdr arg2))))))))
  2868. (when (and (memq (car arg1) '(< <= >= >)) ; (and (op x y) (op x z)) -> (op x (min|max y z))
  2869. (equal? (cadr arg1) (cadr arg2)))
  2870. (if (and (rational? (caddr arg1))
  2871. (rational? (caddr arg2)))
  2872. (return `(,(car arg1)
  2873. ,(cadr arg1)
  2874. ,((if (memq (car arg1) '(< <=)) min max) (caddr arg1) (caddr arg2)))))
  2875. (return `(,(car arg1)
  2876. ,(cadr arg1)
  2877. (,(if (memq (car arg1) '(< <=)) 'min 'max) ,(caddr arg1) ,(caddr arg2)))))
  2878. (when (or (equal? (caddr arg1) (cadr arg2)) ; (and (op x y) (op y z))
  2879. (equal? (cadr arg1) (caddr arg2)) ; (and (op x y) (op z x))
  2880. (and (memq (car arg1) '(= char=? string=? char-ci=? string-ci=?))
  2881. (or (equal? (cadr arg1) (cadr arg2))
  2882. (equal? (caddr arg1) (caddr arg2)))))
  2883. (let ((op1 (car arg1))
  2884. (arg1-1 (cadr arg1))
  2885. (arg1-2 (caddr arg1))
  2886. (arg2-1 (cadr arg2))
  2887. (arg2-2 (caddr arg2)))
  2888. (return
  2889. (cond ((equal? arg1-2 arg2-1) ; (and (op x y) (op y z)) -> (op x y z)
  2890. (if (equal? arg1-1 arg2-2)
  2891. (if (memq op1 '(= char=? string=? char-ci=? string-ci=?))
  2892. arg1
  2893. (and (memq op1 '(<= >= char<=? char>=? string<=? string>=?
  2894. char-ci<=? char-ci>=? string-ci<=? string-ci>=?))
  2895. `(,(case op1
  2896. ((>= <=) '=)
  2897. ((char<= char>=) 'char=?)
  2898. ((char-ci<= char-ci>=) 'char-ci=?)
  2899. ((string<= string>=) 'string=?)
  2900. ((string-ci<= string-ci>=) 'string-ci=?))
  2901. ,@(cdr arg1))))
  2902. (and (or (not (code-constant? arg1-1))
  2903. (not (code-constant? arg2-2))
  2904. ((symbol->value op1) arg1-1 arg2-2))
  2905. `(,op1 ,arg1-1 ,arg2-1 ,arg2-2))))
  2906. ((equal? arg1-1 arg2-2) ; (and (op x y) (op z x)) -> (op z x y)
  2907. (if (equal? arg1-2 arg2-1)
  2908. (and (memq op1 '(= char=? string=? char-ci=? string-ci=?))
  2909. arg1)
  2910. (and (or (not (code-constant? arg2-1))
  2911. (not (code-constant? arg1-2))
  2912. ((symbol->value op1) arg2-1 arg1-2))
  2913. `(,op1 ,arg2-1 ,arg1-1 ,arg1-2))))
  2914. ;; here we're restricted to equalities and we know arg1 != arg2
  2915. ((equal? arg1-1 arg2-1) ; (and (op x y) (op x z)) -> (op x y z)
  2916. (if (and (code-constant? arg1-2)
  2917. (code-constant? arg2-2))
  2918. (and ((symbol->value op1) arg1-2 arg2-2)
  2919. arg1)
  2920. `(,op1 ,arg1-1 ,arg1-2 ,arg2-2)))
  2921. ;; equalities again
  2922. ((and (code-constant? arg1-1)
  2923. (code-constant? arg2-1))
  2924. (and ((symbol->value op1) arg1-1 arg2-1)
  2925. arg1))
  2926. (else `(,op1 ,arg1-1 ,arg1-2 ,arg2-1)))))))
  2927. ;; check some special cases
  2928. (when (and (or (equal? (cadr arg1) (cadr arg2))
  2929. (and (pair? (cddr arg2))
  2930. (null? (cdddr arg2))
  2931. (equal? (cadr arg1) (caddr arg2))))
  2932. (hash-table-ref booleans (car arg1)))
  2933. (when (or (eq? (car arg1) 'zero?) ; perhaps rational? and integer? here -- not many hits
  2934. (eq? (car arg2) 'zero?))
  2935. (if (or (memq (car arg1) '(integer? rational? exact?))
  2936. (memq (car arg2) '(integer? rational? exact?)))
  2937. (return `(eqv? ,(cadr arg1) 0)))
  2938. (if (or (eq? (car arg1) 'inexact?)
  2939. (eq? (car arg2) 'inexact?))
  2940. (return `(eqv? ,(cadr arg1) 0.0))))
  2941. (when (hash-table-ref and-rel-ops (car arg2))
  2942. (when (and (eq? (car arg1) 'symbol?)
  2943. (memq (car arg2) '(eq? eqv? equal?))
  2944. (or (quoted-symbol? (cadr arg2))
  2945. (quoted-symbol? (caddr arg2))))
  2946. (return `(eq? ,@(cdr arg2))))
  2947. (when (and (eq? (car arg1) 'positive?)
  2948. (eq? (car arg2) '<)
  2949. (eq? (cadr arg1) (cadr arg2)))
  2950. (return `(< 0 ,(cadr arg1) ,(caddr arg2))))))
  2951. (when (and (member (cadr arg1) arg2)
  2952. (memq (car arg2) '(string=? char=? eq? eqv? equal?))
  2953. (null? (cdddr arg2))
  2954. (hash-table-ref bools (car arg1))
  2955. (or (and (code-constant? (cadr arg2))
  2956. (compatible? (car arg1) (->lint-type (cadr arg2))))
  2957. (and (code-constant? (caddr arg2))
  2958. (compatible? (car arg1) (->lint-type (caddr arg2))))))
  2959. (return `(,(if (eq? (car arg1) 'char?) ,eqv? 'equal?) ,@(cdr arg2))))
  2960. (when (and (equal? (cadr arg1) (cadr arg2))
  2961. (eq? (car arg1) 'inexact?)
  2962. (eq? (car arg2) 'real?))
  2963. (return `(and ,arg2 ,arg1)))
  2964. ;; this makes some of the code above redundant
  2965. (let ((rel (relsub arg1 arg2 'and env)))
  2966. (if (or (boolean? rel)
  2967. (pair? rel))
  2968. (return rel)))
  2969. ;; (and ... (not...))
  2970. (unless (eq? (car arg1) (car arg2))
  2971. (if (eq? (car arg1) 'not)
  2972. (let ((temp arg1))
  2973. (set! arg1 arg2)
  2974. (set! arg2 temp)))
  2975. (when (and (eq? (car arg2) 'not)
  2976. (pair? (cadr arg2))
  2977. (pair? (cdadr arg2))
  2978. (not (eq? (caadr arg2) 'let?))
  2979. (or (equal? (cadr arg1) (cadadr arg2))
  2980. (and (pair? (cddr arg1))
  2981. (equal? (caddr arg1) (cadadr arg2))))
  2982. (eq? (return-type (car arg1) env) 'boolean?)
  2983. (eq? (return-type (caadr arg2) env) 'boolean?))
  2984. (let ((t2 (and-not-redundant arg1 arg2)))
  2985. (cond ;((not t2) #f)
  2986. ((eq? t2 'contradictory) (return #f))
  2987. ((symbol? t2) (return `(,t2 ,@(cdr arg1))))
  2988. ((pair? t2) (return t2))))))
  2989. (if (hash-table-ref bools (car arg1))
  2990. (let ((p (member (cadr arg1) (cdr arg2))))
  2991. (when p
  2992. (let ((sig (arg-signature (car arg2) env))
  2993. (pos (- (length arg2) (length p))))
  2994. (when (pair? sig)
  2995. (let ((arg-type (and (> (length sig) pos)
  2996. (list-ref sig pos))))
  2997. (unless (compatible? (car arg1) arg-type)
  2998. (let ((ln (and (< 0 line-number 100000) line-number)))
  2999. (format outport "~NCin ~A~A, ~A is ~A, but ~A wants ~A"
  3000. lint-left-margin #\space
  3001. (truncated-list->string form)
  3002. (if ln (format #f " (line ~D)" ln) "")
  3003. (cadr arg1)
  3004. (prettify-checker-unq (car arg1))
  3005. (car arg2)
  3006. (prettify-checker arg-type))))))))))
  3007. (cond ((not (and (eq? (car arg1) 'equal?) ; (and (equal? (car a1) (car a2)) (equal? (cdr a1) (cdr a2))) -> (equal? a1 a2)
  3008. (eq? (car arg2) 'equal?)
  3009. (pair? (cadr arg1))
  3010. (pair? (caddr arg1))
  3011. (pair? (cadr arg2))
  3012. (pair? (caddr arg2))
  3013. (eq? (caadr arg1) (caaddr arg1)))))
  3014. ((assq (caadr arg1)
  3015. '((car cdr #t)
  3016. (caar cdar car) (cadr cddr cdr)
  3017. (caaar cdaar caar) (caadr cdadr cadr) (caddr cdddr cddr) (cadar cddar cdar)
  3018. (cadddr cddddr cdddr) (caaaar cdaaar caaar) (caaadr cdaadr caadr) (caadar cdadar cadar)
  3019. (caaddr cdaddr caddr) (cadaar cddaar cdaar) (cadadr cddadr cdadr) (caddar cdddar cddar)))
  3020. => (lambda (x)
  3021. (if (and (eq? (caadr arg2) (cadr x))
  3022. (eq? (caaddr arg2) (cadr x))
  3023. (equal? (cadadr arg1) (cadadr arg2))
  3024. (equal? (cadr (caddr arg1)) (cadr (caddr arg2))))
  3025. (return (if (symbol? (caddr x))
  3026. `(equal? (,(caddr x) ,(cadadr arg1)) (,(caddr x) ,(cadr (caddr arg1))))
  3027. `(equal? ,(cadadr arg1) ,(cadr (caddr arg1)))))))))
  3028. )))
  3029. ;; len > 3 or nothing was caught above
  3030. (let ((nots 0)
  3031. (revers 0)
  3032. (arglen (- len 1)))
  3033. (for-each (lambda (a)
  3034. (if (pair? a)
  3035. (if (eq? (car a) 'not)
  3036. (set! nots (+ nots 1))
  3037. (if (hash-table-ref notables (car a))
  3038. (set! revers (+ revers 1))))))
  3039. (cdr form))
  3040. (if (= nots arglen) ; every arg is `(not ...)
  3041. (let ((nf (simplify-boolean `(or ,@(map cadr (cdr form))) () () env)))
  3042. (return (simplify-boolean `(not ,nf) () () env)))
  3043. (if (and (> arglen 2)
  3044. (or (>= nots (/ (* 3 arglen) 4)) ; > 2/3 seems to get some ugly rewrites
  3045. (and (>= nots (/ (* 2 arglen) 3)) ; was > 1/2 here
  3046. (> revers 0))))
  3047. (let ((nf (simplify-boolean `(or ,@(map (lambda (p)
  3048. (cond ((not (pair? p))
  3049. `(not ,p))
  3050. ((eq? (car p) 'not)
  3051. (cadr p))
  3052. ((hash-table-ref notables (car p)) =>
  3053. (lambda (op)
  3054. `(,op ,@(cdr p))))
  3055. (else `(not ,p))))
  3056. (cdr form)))
  3057. () () env)))
  3058. (return (simplify-boolean `(not ,nf) () () env))))))
  3059. (if (every? (lambda (a)
  3060. (and (pair? a)
  3061. (eq? (car a) 'zero?)))
  3062. (cdr form))
  3063. (return `(= 0 ,@(map cadr (cdr form)))))
  3064. (let ((diff (apply and-redundants env (cdr form))))
  3065. (when diff
  3066. (if (null? (cdr diff))
  3067. (return (car diff)))
  3068. (return (simplify-boolean `(and ,@diff) () () env))))
  3069. ;; now there are redundancies below (see subsumes?) but they assumed the tests were side-by-side
  3070. (do ((new-form ())
  3071. (retry #f)
  3072. (exprs (cdr form) (cdr exprs)))
  3073. ((null? exprs)
  3074. (or (null? new-form) ; (and) -> #t
  3075. (let ((newer-form (let ((nform (reverse new-form)))
  3076. (map (lambda (x cdr-x)
  3077. (if (and x (code-constant? x))
  3078. (values)
  3079. x))
  3080. nform (cdr nform)))))
  3081. (return
  3082. (cond ((null? newer-form)
  3083. (car new-form))
  3084. ((and (eq? (car new-form) #t) ; trailing #t is dumb if next-to-last is boolean func
  3085. (pair? (cdr new-form))
  3086. (pair? (cadr new-form))
  3087. (symbol? (caadr new-form))
  3088. (eq? (return-type (caadr new-form) env) 'boolean?))
  3089. (if (null? (cdr newer-form))
  3090. (car newer-form)
  3091. `(and ,@newer-form)))
  3092. (retry
  3093. (simplify-boolean `(and ,@newer-form ,(car new-form)) () () env))
  3094. (else `(and ,@newer-form ,(car new-form))))))))
  3095. (let* ((e (car exprs))
  3096. (val (classify e))
  3097. (old-form new-form))
  3098. (if (and (pair? val)
  3099. (memq (car val) '(and or not)))
  3100. (set! val (classify (set! e (simplify-boolean val () false env))))
  3101. (when (and (> len 3)
  3102. (pair? (cdr exprs)))
  3103. (if (symbol? val)
  3104. (if (and (pair? (cadr exprs))
  3105. (memq val (cadr exprs)))
  3106. (let ((nval (simplify-boolean `(and ,val ,(cadr exprs)) () false env)))
  3107. (if (and (pair? nval)
  3108. (eq? (car nval) 'and))
  3109. (and-incomplete form 'and val (cadr exprs) env)
  3110. (begin
  3111. (set! val nval)
  3112. (set! exprs (cdr exprs)))))
  3113. (do ((ip (cdr exprs) (cdr ip))
  3114. (found-it #f))
  3115. ((or found-it
  3116. (not (pair? ip))))
  3117. (do ((p (car ip) (cdr p)))
  3118. ((or (not (pair? p))
  3119. (and (memq val p)
  3120. (let ((nval (simplify-boolean `(and ,val ,p) () false env)))
  3121. (if (and (pair? nval)
  3122. (eq? (car nval) 'and))
  3123. (set! found-it p)
  3124. (let ((ln (and (< 0 line-number 100000) line-number)))
  3125. (format outport "~NCin ~A~A,~%~NCperhaps change ~S to ~S~%"
  3126. lint-left-margin #\space
  3127. (truncated-list->string form)
  3128. (if ln (format #f " (line ~D)" ln) "")
  3129. (+ lint-left-margin 4) #\space
  3130. `(and ... ,val ... ,p)
  3131. nval)
  3132. (set! found-it #t)))))
  3133. (and (pair? (car p))
  3134. (memq val (car p))
  3135. (set! found-it (car p))))
  3136. (if (pair? found-it)
  3137. (and-incomplete form 'and val found-it env))))))
  3138. (when (and (pair? val)
  3139. (pair? (cadr exprs))
  3140. (hash-table-ref bools (car val)))
  3141. (if (member (cadr val) (cadr exprs))
  3142. (and-forgetful form 'and val (cadr exprs) env)
  3143. (do ((p (cadr exprs) (cdr p)))
  3144. ((or (not (pair? p))
  3145. (and (pair? (car p))
  3146. (member (cadr val) (car p))))
  3147. (if (pair? p)
  3148. (and-forgetful form 'and val (car p) env)))))))))
  3149. (if (not (or retry
  3150. (equal? e (car exprs))))
  3151. (set! retry #t))
  3152. ;(format *stderr* "val: ~A, e: ~A~%" val e)
  3153. ;; (and x1 x2 x1) is not reducible
  3154. ;; the final thing has to remain at the end, but can be deleted earlier if it can't short-circuit the evaluation,
  3155. ;; but if there are expressions following the first x1, we can't be sure that it is not
  3156. ;; protecting them:
  3157. ;; (and false-or-0 (display (list-ref lst false-or-0)) false-or-0)
  3158. ;; so I'll not try to optimize that case. But (and x x) is optimizable.
  3159. (cond ((eq? val #t)
  3160. (if (null? (cdr exprs)) ; (and x y #t) should not remove the #t
  3161. (if (or (and (pair? e)
  3162. (eq? (return-type (car e) env) 'boolean?))
  3163. (eq? e #t))
  3164. (set! new-form (cons val new-form))
  3165. (if (or (null? new-form)
  3166. (not (equal? e (car new-form))))
  3167. (set! new-form (cons e new-form))))
  3168. (if (and (not (eq? e #t))
  3169. (or (null? new-form)
  3170. (not (member e new-form))))
  3171. (set! new-form (cons e new-form)))))
  3172. ((not val) ; #f in 'and' ends the expression
  3173. (set! new-form (if (or (null? new-form)
  3174. (just-symbols? new-form))
  3175. '(#f)
  3176. (cons #f new-form)))
  3177. (set! exprs '(#f)))
  3178. ((and (pair? e) ; if (and ...) splice into current
  3179. (eq? (car e) 'and))
  3180. (set! exprs (append e (cdr exprs))))
  3181. ((and (pair? e) ; (and (list? p) (pair? p) ...) -> (and (pair? p) ...)
  3182. (pair? (cdr exprs))
  3183. (pair? (cadr exprs))
  3184. (eq? (and-redundant? e (cadr exprs)) (caadr exprs))
  3185. (equal? (cadr e) (cadadr exprs))))
  3186. ((and (pair? e) ; (and (list? p) (not (null? p)) ...) -> (and (pair? p) ...)
  3187. (memq (car e) '(list? pair?))
  3188. (pair? (cdr exprs))
  3189. (let ((p (cadr exprs)))
  3190. (and (pair? p)
  3191. (eq? (car p) 'not)
  3192. (pair? (cadr p))
  3193. (eq? (caadr p) 'null?)
  3194. (equal? (cadr e) (cadadr p)))))
  3195. (set! new-form (cons `(pair? ,(cadr e)) new-form))
  3196. (set! exprs (cdr exprs)))
  3197. ((not (and (pair? e) ; (and ... (or ... 123) ...) -> splice out or
  3198. (pair? (cdr exprs))
  3199. (eq? (car e) 'or)
  3200. (pair? (cdr e))
  3201. (pair? (cddr e))
  3202. (cond ((list-ref e (- (length e) 1)) => code-constant?) ; (or ... #f)
  3203. (else #f))))
  3204. (if (not (and (pair? new-form)
  3205. (or (eq? val (car new-form)) ; omit repeated tests
  3206. (and (pair? val) ; and redundant tests
  3207. (hash-table-ref booleans (car val))
  3208. (any? (lambda (p)
  3209. (and (pair? p)
  3210. (subsumes? (car val) (car p))
  3211. (equal? (cadr val) (cadr p))))
  3212. new-form)))))
  3213. (set! new-form (cons val new-form)))))
  3214. (if (and (not (eq? new-form old-form))
  3215. (pair? (cdr new-form)))
  3216. (let ((rel (relsub (car new-form) (cadr new-form) 'and env)))
  3217. ;; rel #f should halt everything as above, and it looks ugly in the output,
  3218. ;; but it never happens in real code
  3219. (if (or (pair? rel)
  3220. (boolean? rel))
  3221. (set! new-form (cons rel (cddr new-form))))))))))))))))))))))))
  3222. (define (splice-if f lst)
  3223. (cond ((null? lst) ())
  3224. ((not (pair? lst)) lst)
  3225. ((and (pair? (car lst))
  3226. (f (caar lst)))
  3227. (append (splice-if f (cdar lst))
  3228. (splice-if f (cdr lst))))
  3229. (else (cons (car lst)
  3230. (splice-if f (cdr lst))))))
  3231. (define (horners-rule form)
  3232. (and (pair? form)
  3233. (call-with-exit
  3234. (lambda (return)
  3235. (do ((p form (cdr p))
  3236. (coeffs #f)
  3237. (top 0)
  3238. (sym #f))
  3239. ((not (pair? p))
  3240. (do ((x (- top 1) (- x 1))
  3241. (result (coeffs top)))
  3242. ((< x 0)
  3243. result)
  3244. (set! result
  3245. (if (zero? (coeffs x))
  3246. `(* ,sym ,result)
  3247. `(+ ,(coeffs x) (* ,sym ,result))))))
  3248. (let ((cx (car p)))
  3249. (cond ((number? cx)
  3250. (if (not coeffs) (set! coeffs (make-vector 4 0)))
  3251. (set! (coeffs 0) (+ (coeffs 0) cx)))
  3252. ((symbol? cx)
  3253. (if (not sym)
  3254. (set! sym cx)
  3255. (if (not (eq? sym cx))
  3256. (return #f)))
  3257. (if (not coeffs) (set! coeffs (make-vector 4 0)))
  3258. (set! top (max top 1))
  3259. (set! (coeffs 1) (+ (coeffs 1) 1)))
  3260. ((not (and (pair? cx)
  3261. (eq? (car cx) '*)))
  3262. (return #f))
  3263. (else
  3264. (let ((ctr 0)
  3265. (ax 1))
  3266. (for-each (lambda (qx)
  3267. (if (symbol? qx)
  3268. (if (not sym)
  3269. (begin
  3270. (set! sym qx)
  3271. (set! ctr 1))
  3272. (if (not (eq? sym qx))
  3273. (return #f)
  3274. (set! ctr (+ ctr 1))))
  3275. (if (number? qx)
  3276. (set! ax (* ax qx))
  3277. (return #f))))
  3278. (cdr cx))
  3279. (if (not coeffs) (set! coeffs (make-vector 4 0)))
  3280. (if (>= ctr (length coeffs))
  3281. (set! coeffs (copy coeffs (make-vector (* ctr 2) 0))))
  3282. (set! top (max top ctr))
  3283. (set! (coeffs ctr) (+ (coeffs ctr) ax)))))))))))
  3284. (define (simplify-numerics form env)
  3285. ;; this returns a form, possibly the original simplified
  3286. (let ((real-result? (lambda (op) (memq op '(imag-part real-part abs magnitude angle max min exact->inexact inexact
  3287. modulo remainder quotient lcm gcd))))
  3288. (rational-result? (lambda (op) (memq op '(rationalize inexact->exact exact))))
  3289. (integer-result? (lambda (op) (memq op '(logior lognot logxor logand numerator denominator floor round truncate ceiling ash)))))
  3290. (define (inverse-op op)
  3291. (case op
  3292. ((sin) 'asin) ((cos) 'acos) ((tan) 'atan) ((asin) 'sin) ((acos) 'cos) ((atan) 'tan)
  3293. ((sinh) 'asinh) ((cosh) 'acosh) ((tanh) 'atanh) ((asinh) 'sinh) ((acosh) 'cosh) ((atanh) 'tanh)
  3294. ((log) 'exp) ((exp) 'log)))
  3295. (define (just-rationals? form)
  3296. (or (null? form)
  3297. (rational? form)
  3298. (and (pair? form)
  3299. (rational? (car form))
  3300. (just-rationals? (cdr form)))))
  3301. (define (just-reals? form)
  3302. (or (null? form)
  3303. (real? form)
  3304. (and (pair? form)
  3305. (real? (car form))
  3306. (just-reals? (cdr form)))))
  3307. (define (just-integers? form)
  3308. (or (null? form)
  3309. (integer? form)
  3310. (and (pair? form)
  3311. (integer? (car form))
  3312. (just-integers? (cdr form)))))
  3313. (define (simplify-arg x)
  3314. (if (or (null? x) ; constants and the like look dumb if simplified
  3315. (not (proper-list? x))
  3316. (not (hash-table-ref no-side-effect-functions (car x)))
  3317. (var-member (car x) env))
  3318. x
  3319. (let ((f (simplify-numerics x env)))
  3320. (if (and (pair? f)
  3321. (just-rationals? f))
  3322. (catch #t
  3323. (lambda ()
  3324. (eval f))
  3325. (lambda ignore f))
  3326. f))))
  3327. (define (remove-inexactions val)
  3328. (when (and (or (assq 'exact->inexact val)
  3329. (assq 'inexact val))
  3330. (not (tree-member 'random val))
  3331. (any? number? val))
  3332. (set! val (map (lambda (x)
  3333. (if (and (pair? x)
  3334. (memq (car x) '(inexact exact->inexact)))
  3335. (cadr x)
  3336. x))
  3337. val))
  3338. (if (not (any? (lambda (x)
  3339. (and (number? x)
  3340. (inexact? x)))
  3341. val))
  3342. (do ((p val (cdr p)))
  3343. ((or (null? p)
  3344. (number? (car p)))
  3345. (if (pair? p)
  3346. (set-car! p (* 1.0 (car p))))))))
  3347. val)
  3348. ;; polar notation (@) is never used anywhere except test suites
  3349. (let* ((args (map simplify-arg (cdr form)))
  3350. (len (length args)))
  3351. (case (car form)
  3352. ((+)
  3353. (case len
  3354. ((0) 0)
  3355. ((1) (car args))
  3356. (else
  3357. (let ((val (remove-all 0 (splice-if (lambda (x) (eq? x '+)) args))))
  3358. (if (every? (lambda (x) (or (not (number? x)) (rational? x))) val)
  3359. (let ((rats (collect-if list rational? val)))
  3360. (if (and (pair? rats)
  3361. (pair? (cdr rats)))
  3362. (let ((y (apply + rats)))
  3363. (set! val (if (zero? y)
  3364. (collect-if list (lambda (x) (not (number? x))) val)
  3365. (cons y (collect-if list (lambda (x) (not (number? x))) val))))))))
  3366. (set! val (remove-inexactions val))
  3367. (if (any? (lambda (p) ; collect all + and - vals -> (- (+ ...) ...)
  3368. (and (pair? p)
  3369. (eq? (car p) '-)))
  3370. val)
  3371. (let ((plus ())
  3372. (minus ())
  3373. (c 0))
  3374. (for-each (lambda (p)
  3375. (if (not (and (pair? p)
  3376. (eq? (car p) '-)))
  3377. (if (rational? p)
  3378. (set! c (+ c p))
  3379. (set! plus (cons p plus)))
  3380. (if (null? (cddr p))
  3381. (if (rational? (cadr p))
  3382. (set! c (- c (cadr p)))
  3383. (set! minus (cons (cadr p) minus)))
  3384. (begin
  3385. (if (rational? (cadr p))
  3386. (set! c (+ c (cadr p)))
  3387. (set! plus (cons (cadr p) plus)))
  3388. (for-each (lambda (p1)
  3389. (if (rational? p1)
  3390. (set! c (- c p1))
  3391. (set! minus (cons p1 minus))))
  3392. (cddr p))))))
  3393. val)
  3394. (simplify-numerics `(- (+ ,@(reverse plus) ,@(if (positive? c) (list c) ()))
  3395. ,@(reverse minus) ,@(if (negative? c) (list (abs c)) ()))
  3396. env))
  3397. (case (length val)
  3398. ((0) 0)
  3399. ((1) (car val)) ; (+ x) -> x
  3400. ((2)
  3401. (let ((arg1 (car val))
  3402. (arg2 (cadr val)))
  3403. (cond ((and (real? arg2) ; (+ x -1) -> (- x 1)
  3404. (negative? arg2)
  3405. (not (number? arg1)))
  3406. `(- ,arg1 ,(abs arg2)))
  3407. ((and (real? arg1) ; (+ -1 x) -> (- x 1)
  3408. (negative? arg1)
  3409. (not (number? arg2)))
  3410. `(- ,arg2 ,(abs arg1)))
  3411. ((and (pair? arg1)
  3412. (eq? (car arg1) '*) ; (+ (* a b) (* a c)) -> (* a (+ b c))
  3413. (pair? arg2)
  3414. (eq? (car arg2) '*)
  3415. (any? (lambda (a)
  3416. (member a (cdr arg2)))
  3417. (cdr arg1)))
  3418. (do ((times ())
  3419. (pluses ())
  3420. (rset (cdr arg2))
  3421. (p (cdr arg1) (cdr p)))
  3422. ((null? p)
  3423. ;; times won't be () because we checked above for a match
  3424. ;; if pluses is (), arg1 is completely included in arg2
  3425. ;; if rset is (), arg2 is included in arg1
  3426. (simplify-numerics `(* ,@(reverse times)
  3427. (+ (* ,@(reverse (if (pair? pluses) pluses (list (if (null? pluses) 1 pluses)))))
  3428. (* ,@rset)))
  3429. env))
  3430. (if (member (car p) rset)
  3431. (begin
  3432. (set! times (cons (car p) times))
  3433. (set! rset (remove (car p) rset)))
  3434. (set! pluses (cons (car p) pluses)))))
  3435. ((and (pair? arg1) (eq? (car arg1) '/) ; (+ (/ a b) (/ c b)) -> (/ (+ a c) b)
  3436. (pair? arg2) (eq? (car arg2) '/)
  3437. (pair? (cddr arg1)) (pair? (cddr arg2))
  3438. (equal? (cddr arg1) (cddr arg2)))
  3439. `(/ (+ ,(cadr arg1) ,(cadr arg2)) ,@(cddr arg1)))
  3440. (else `(+ ,@val)))))
  3441. (else
  3442. (or (horners-rule val)
  3443. ;; not many cases here, oddly enough, Horner's rule gets most
  3444. ;; (+ (/ (f x) 3) (/ (g x) 3) (/ (h x) 3) 15) [ignoring problems involving overflow]
  3445. `(+ ,@val)))))))))
  3446. ((*)
  3447. (case len
  3448. ((0) 1)
  3449. ((1) (car args))
  3450. (else
  3451. (let ((val (remove-all 1 (splice-if (lambda (x) (eq? x '*)) args))))
  3452. (if (every? (lambda (x) (or (not (number? x)) (rational? x))) val)
  3453. (let ((rats (collect-if list rational? val)))
  3454. (if (and (pair? rats)
  3455. (pair? (cdr rats)))
  3456. (let ((y (apply * rats)))
  3457. (set! val (if (= y 1)
  3458. (collect-if list (lambda (x) (not (number? x))) val)
  3459. (cons y (collect-if list (lambda (x) (not (number? x))) val))))))))
  3460. (set! val (remove-inexactions val))
  3461. (case (length val)
  3462. ((0) 1)
  3463. ((1) (car val)) ; (* x) -> x
  3464. ((2)
  3465. (let ((arg1 (car val))
  3466. (arg2 (cadr val)))
  3467. (cond ((just-rationals? val)
  3468. (let ((new-val (apply * val))) ; huge numbers here are less readable
  3469. (if (< (abs new-val) 1000000)
  3470. new-val
  3471. `(* ,@val))))
  3472. ((memv 0 val) ; (* x 0) -> 0
  3473. 0)
  3474. ((memv -1 val)
  3475. `(- ,@(remove -1 val))) ; (* -1 x) -> (- x)
  3476. ((not (pair? arg2))
  3477. `(* ,@val))
  3478. ((pair? arg1)
  3479. (let ((op1 (car arg1))
  3480. (op2 (car arg2)))
  3481. (cond ((and (eq? op1 '-) ; (* (- x) (- y)) -> (* x y)
  3482. (null? (cddr arg1))
  3483. (eq? op2 '-)
  3484. (null? (cddr arg2)))
  3485. `(* ,(cadr arg1) ,(cadr arg2)))
  3486. ((and (eq? op1 '/) ; (* (/ x) (/ y)) -> (/ (* x y)) etc
  3487. (eq? op2 '/))
  3488. (let ((op1-arg1 (cadr arg1))
  3489. (op2-arg1 (cadr arg2)))
  3490. (if (null? (cddr arg1))
  3491. (if (null? (cddr arg2))
  3492. `(/ (* ,op1-arg1 ,op2-arg1))
  3493. (if (equal? op1-arg1 op2-arg1)
  3494. `(/ ,(caddr arg2))
  3495. (simplify-numerics `(/ ,op2-arg1 (* ,op1-arg1 ,(caddr arg2))) env)))
  3496. (if (null? (cddr arg2))
  3497. (if (equal? op1-arg1 op2-arg1)
  3498. `(/ ,(caddr arg1))
  3499. (simplify-numerics `(/ ,op1-arg1 (* ,(caddr arg1) ,op2-arg1)) env))
  3500. (simplify-numerics `(/ (* ,op1-arg1 ,op2-arg1) (* ,@(cddr arg1) ,@(cddr arg2))) env)))))
  3501. ((and (= (length arg1) 3)
  3502. (equal? (cdr arg1) (cdr arg2))
  3503. (case op1
  3504. ((gcd) (eq? op2 'lcm))
  3505. ((lcm) (eq? op2 'gcd))
  3506. (else #f)))
  3507. `(abs (* ,@(cdr arg1)))) ; (* (gcd a b) (lcm a b)) -> (abs (* a b)) but only if 2 args?
  3508. ((and (eq? op1 'exp) ; (* (exp a) (exp b)) -> (exp (+ a b))
  3509. (eq? op2 'exp))
  3510. `(exp (+ ,(cadr arg1) ,(cadr arg2))))
  3511. ((and (eq? op1 'sqrt) ; (* (sqrt x) (sqrt y)) -> (sqrt (* x y))
  3512. (eq? op2 'sqrt))
  3513. `(sqrt (* ,(cadr arg1) ,(cadr arg2))))
  3514. ((not (and (eq? op1 'expt) (eq? op2 'expt)))
  3515. `(* ,@val))
  3516. ((equal? (cadr arg1) (cadr arg2)) ; (* (expt x y) (expt x z)) -> (expt x (+ y z))
  3517. `(expt ,(cadr arg1) (+ ,(caddr arg1) ,(caddr arg2))))
  3518. ((equal? (caddr arg1) (caddr arg2)) ; (* (expt x y) (expt z y)) -> (expt (* x z) y)
  3519. `(expt (* ,(cadr arg1) ,(cadr arg2)) ,(caddr arg1)))
  3520. (else `(* ,@val)))))
  3521. ((and (number? arg1) ; (* 2 (random 3.0)) -> (random 6.0)
  3522. (eq? (car arg2) 'random)
  3523. (number? (cadr arg2))
  3524. (not (rational? (cadr arg2))))
  3525. `(random ,(* arg1 (cadr arg2))))
  3526. (else `(* ,@val)))))
  3527. (else
  3528. (cond ((just-rationals? val)
  3529. (let ((new-val (apply * val))) ; huge numbers here are less readable
  3530. (if (< (abs new-val) 1000000)
  3531. new-val
  3532. `(* ,@val))))
  3533. ((memv 0 val) ; (* x 0 2) -> 0
  3534. 0)
  3535. ((memv -1 val)
  3536. `(- (* ,@(remove -1 val)))) ; (* -1 x y) -> (- (* x y))
  3537. ((any? (lambda (p) ; collect * and / vals -> (/ (* ...) ...)
  3538. (and (pair? p)
  3539. (eq? (car p) '/)))
  3540. val)
  3541. (let ((mul ())
  3542. (div ()))
  3543. (for-each (lambda (p)
  3544. (if (not (and (pair? p)
  3545. (eq? (car p) '/)))
  3546. (set! mul (cons p mul))
  3547. (if (null? (cddr p))
  3548. (set! div (cons (cadr p) div))
  3549. (begin
  3550. (set! mul (cons (cadr p) mul))
  3551. (set! div (append (cddr p) div))))))
  3552. val)
  3553. (for-each (lambda (n)
  3554. (when (member n div)
  3555. (set! div (remove n div))
  3556. (set! mul (remove n mul))))
  3557. (copy mul))
  3558. (let ((expr (if (null? mul)
  3559. (if (null? div)
  3560. `(*) ; for simplify-numerics' benefit
  3561. `(/ 1 ,@(reverse div)))
  3562. (if (null? div)
  3563. `(* ,@(reverse mul))
  3564. `(/ (* ,@(reverse mul)) ,@(reverse div))))))
  3565. (simplify-numerics expr env))))
  3566. (else `(* ,@val)))))))))
  3567. ((-)
  3568. (set! args (remove-inexactions args))
  3569. (case len
  3570. ((0) form)
  3571. ((1) ; negate
  3572. (if (number? (car args))
  3573. (- (car args))
  3574. (if (not (list? (car args)))
  3575. `(- ,@args)
  3576. (case (length (car args))
  3577. ((2) (if (eq? (caar args) '-)
  3578. (cadar args) ; (- (- x)) -> x
  3579. `(- ,@args)))
  3580. ((3) (if (eq? (caar args) '-)
  3581. `(- ,(caddar args) ,(cadar args)) ; (- (- x y)) -> (- y x)
  3582. `(- ,@args)))
  3583. (else `(- ,@args))))))
  3584. ((2)
  3585. (let ((arg1 (car args))
  3586. (arg2 (cadr args)))
  3587. (cond ((just-rationals? args) (apply - args)) ; (- 3 2) -> 1
  3588. ((eqv? arg1 0) `(- ,arg2)) ; (- 0 x) -> (- x)
  3589. ((eqv? arg2 0) arg1) ; (- x 0) -> x
  3590. ((equal? arg1 arg2) 0) ; (- x x) -> 0
  3591. ((and (pair? arg2)
  3592. (eq? (car arg2) '-)
  3593. (pair? (cdr arg2)))
  3594. (if (null? (cddr arg2))
  3595. `(+ ,arg1 ,(cadr arg2)) ; (- x (- y)) -> (+ x y)
  3596. (simplify-numerics `(- (+ ,arg1 ,@(cddr arg2)) ,(cadr arg2)) env))) ; (- x (- y z)) -> (- (+ x z) y)
  3597. ((and (pair? arg2) ; (- x (+ y z)) -> (- x y z)
  3598. (eq? (car arg2) '+))
  3599. (simplify-numerics `(- ,arg1 ,@(cdr arg2)) env))
  3600. ((and (pair? arg1) ; (- (- x y) z) -> (- x y z)
  3601. (eq? (car arg1) '-))
  3602. (if (> (length arg1) 2)
  3603. `(- ,@(cdr arg1) ,arg2)
  3604. (simplify-numerics `(- (+ ,(cadr arg1) ,arg2)) env))) ; (- (- x) y) -> (- (+ x y))
  3605. ((and (pair? arg2) ; (- x (truncate x)) -> (remainder x 1)
  3606. (eq? (car arg2) 'truncate)
  3607. (equal? arg1 (cadr arg2)))
  3608. `(remainder ,arg1 1))
  3609. ((and (real? arg2) ; (- x -1) -> (+ x 1)
  3610. (negative? arg2)
  3611. (not (number? arg1)))
  3612. `(+ ,arg1 ,(abs arg2)))
  3613. (else `(- ,@args)))))
  3614. (else
  3615. (if (just-rationals? args)
  3616. (apply - args)
  3617. (let ((val (remove-all 0 (splice-if (lambda (x) (eq? x '+)) (cdr args)))))
  3618. (if (every? (lambda (x) (or (not (number? x)) (rational? x))) val)
  3619. (let ((rats (collect-if list rational? val)))
  3620. (if (and (pair? rats)
  3621. (pair? (cdr rats)))
  3622. (let ((y (apply + rats)))
  3623. (set! val (if (zero? y)
  3624. (collect-if list (lambda (x) (not (number? x))) val)
  3625. (cons y (collect-if list (lambda (x) (not (number? x))) val))))))))
  3626. (set! val (cons (car args) val))
  3627. (let ((first-arg (car args))
  3628. (nargs (cdr val)))
  3629. (if (member first-arg nargs)
  3630. (begin
  3631. (set! nargs (remove first-arg nargs)) ; remove once
  3632. (set! first-arg 0)))
  3633. (cond ((null? nargs) first-arg) ; (- x 0 0 0)?
  3634. ((eqv? first-arg 0)
  3635. (if (null? (cdr nargs))
  3636. (if (number? (car nargs))
  3637. (- (car nargs))
  3638. `(- ,(car nargs))) ; (- 0 0 0 x)?
  3639. `(- (+ ,@nargs)))) ; (- 0 z y) -> (- (+ x y))
  3640. ((not (and (pair? (car args))
  3641. (eq? (caar args) '-)))
  3642. `(- ,@(cons first-arg nargs)))
  3643. ((> (length (car args)) 2) ; (- (- x y) z w) -> (- x y z w)
  3644. (simplify-numerics `(- ,@(cdar args) ,@(cdr args)) env))
  3645. (else (simplify-numerics `(- (+ ,(cadar args) ,@(cdr args))) env)))))))))
  3646. ((/)
  3647. (set! args (remove-inexactions args))
  3648. (case len
  3649. ((0) form)
  3650. ((1) ; invert
  3651. (if (number? (car args))
  3652. (if (zero? (car args))
  3653. `(/ ,(car args))
  3654. (/ (car args)))
  3655. (if (not (pair? (car args)))
  3656. `(/ ,@args)
  3657. (case (caar args)
  3658. ((/)
  3659. (case (length (car args))
  3660. ((2) ; (/ (/ x)) -> x
  3661. (cadar args))
  3662. ((3) ; (/ (/ z x)) -> (/ x z)
  3663. `(/ ,@(reverse (cdar args))))
  3664. (else
  3665. (if (eqv? (cadar args) 1)
  3666. `(* ,@(cddar args)) ; (/ (/ 1 x y)) -> (* x y)
  3667. `(/ (* ,@(cddar args)) ,(cadar args)))))) ; (/ (/ z x y)) -> (/ (* x y) z)
  3668. ((expt) ; (/ (expt x y)) -> (expt x (- y))
  3669. `(expt ,(cadar args) (- ,(caddar args))))
  3670. ((exp) ; (/ (exp x)) -> (exp (- x))
  3671. `(exp (- ,(cadar args))))
  3672. (else `(/ ,@args))))))
  3673. ((2)
  3674. (if (and (just-rationals? args)
  3675. (not (zero? (cadr args))))
  3676. (apply / args) ; including (/ 0 12) -> 0
  3677. (let ((arg1 (car args))
  3678. (arg2 (cadr args)))
  3679. (let ((op1 (and (pair? arg1) (car arg1)))
  3680. (op2 (and (pair? arg2) (car arg2))))
  3681. (let ((op1-arg1 (and op1 (pair? (cdr arg1)) (cadr arg1)))
  3682. (op2-arg1 (and op2 (pair? (cdr arg2)) (cadr arg2))))
  3683. (cond ((eqv? arg1 1) ; (/ 1 x) -> (/ x)
  3684. (simplify-numerics `(/ ,arg2) env))
  3685. ((eqv? arg2 1) ; (/ x 1) -> x
  3686. arg1)
  3687. ((and (pair? arg1) ; (/ (/ a b) c) -> (/ a b c)
  3688. (eq? op1 '/)
  3689. (pair? (cddr arg1))
  3690. (not (and (pair? arg2)
  3691. (eq? op2 '/))))
  3692. `(/ ,op1-arg1 ,@(cddr arg1) ,arg2))
  3693. ((and (pair? arg1) ; (/ (/ a) (/ b)) -> (/ b a)??
  3694. (eq? op1 '/)
  3695. (pair? arg2)
  3696. (eq? '/ op2))
  3697. (let ((a1 (if (null? (cddr arg1)) (list 1 op1-arg1) (cdr arg1)))
  3698. (a2 (if (null? (cddr arg2)) (list 1 op2-arg1) (cdr arg2))))
  3699. (simplify-numerics `(/ (* ,(car a1) ,@(cdr a2)) (* ,@(cdr a1) ,(car a2))) env)))
  3700. ((and (pair? arg2)
  3701. (eq? op2 '*)
  3702. (not (side-effect? arg1 env))
  3703. (member arg1 (cdr arg2)))
  3704. (let ((n (remove arg1 (cdr arg2))))
  3705. (if (and (pair? n) (null? (cdr n)))
  3706. `(/ ,@n) ; (/ x (* y x)) -> (/ y)
  3707. `(/ 1 ,@n)))) ; (/ x (* y x z)) -> (/ 1 y z)
  3708. ((and (pair? arg2) ; (/ c (/ a b)) -> (/ (* c b) a)
  3709. (eq? op2 '/))
  3710. (cond ((null? (cddr arg2))
  3711. `(* ,arg1 ,op2-arg1)) ; ignoring divide by zero here (/ x (/ y)) -> (* x y)
  3712. ((eqv? op2-arg1 1)
  3713. `(* ,arg1 ,@(cddr arg2))) ; (/ x (/ 1 y z)) -> (* x y z) -- these never actually happen
  3714. ((not (pair? (cddr arg2)))
  3715. `(/ ,@args)) ; no idea...
  3716. ((and (rational? arg1)
  3717. (rational? op2-arg1)
  3718. (null? (cdddr arg2)))
  3719. (let ((val (/ arg1 op2-arg1)))
  3720. (if (= val 1)
  3721. (caddr arg2)
  3722. (if (= val -1)
  3723. `(- ,(caddr arg2))
  3724. `(* ,val ,(caddr arg2))))))
  3725. (else `(/ (* ,arg1 ,@(cddr arg2)) ,op2-arg1))))
  3726. #|
  3727. ;; can't decide about this -- result usually looks cruddy
  3728. ((and (pair? arg2) ; (/ x (* y z)) -> (/ x y z)
  3729. (eq? op2 '*))
  3730. `(/ ,arg1 ,@(cdr arg2)))
  3731. |#
  3732. ((and (pair? arg1) ; (/ (log x) (log y)) -> (log x y) -- (log number) for (log y) never happens
  3733. (pair? arg2)
  3734. (= (length arg1) (length arg2) 2)
  3735. (case op1
  3736. ((log) (eq? op2 'log))
  3737. ((sin)
  3738. (and (eq? op2 'cos)
  3739. (equal? op1-arg1 op2-arg1)))
  3740. (else #f)))
  3741. (if (eq? op1 'log)
  3742. `(log ,op1-arg1 ,op2-arg1)
  3743. `(tan ,op1-arg1)))
  3744. ((and (pair? arg1) ; (/ (- x) (- y)) -> (/ x y)
  3745. (pair? arg2)
  3746. (eq? op1 '-)
  3747. (eq? op2 '-)
  3748. (= (length arg1) (length arg2) 2))
  3749. `(/ ,op1-arg1 ,op2-arg1))
  3750. ((and (pair? arg1) ; (/ (* x y) (* z y)) -> (/ x z)
  3751. (pair? arg2)
  3752. (eq? op1 '*)
  3753. (case op2
  3754. ((*)
  3755. (and (= (length arg1) (length arg2) 3)
  3756. (equal? (caddr arg1) (caddr arg2))))
  3757. ((log)
  3758. (cond ((assq 'log (cdr arg1))
  3759. => (lambda (p)
  3760. (= (length p) 2)))
  3761. (else #f)))
  3762. (else #f)) ; (/ (* 12 (log x)) (log 2)) -> (* 12 (log x 2))
  3763. (if (eq? op2 '*)
  3764. `(/ ,op1-arg1 ,op2-arg1)
  3765. (let ((used-log op2-arg1))
  3766. `(* ,@(map (lambda (p)
  3767. (if (and used-log
  3768. (pair? p)
  3769. (eq? (car p) 'log))
  3770. (let ((val `(log ,(cadr p) ,used-log)))
  3771. (set! used-log #f)
  3772. val)
  3773. p))
  3774. (cdr arg1)))))))
  3775. ((and (pair? arg1) ; (/ (sqrt x) x) -> (/ (sqrt x))
  3776. (eq? (car arg1) 'sqrt)
  3777. (equal? (cadr arg1) arg2))
  3778. `(/ ,arg1))
  3779. ((and (pair? arg2) ; (/ x (sqrt x)) -> (sqrt x)
  3780. (eq? (car arg2) 'sqrt)
  3781. (equal? (cadr arg2) arg1))
  3782. arg2)
  3783. (else `(/ ,@args))))))))
  3784. (else
  3785. (if (and (just-rationals? args)
  3786. (not (memv 0 (cdr args)))
  3787. (not (memv 0.0 (cdr args))))
  3788. (apply / args)
  3789. (let ((nargs ; (/ x a (* b 1 c) d) -> (/ x a b c d)
  3790. (remove-all 1 (splice-if (lambda (x) (eq? x '*)) (cdr args)))))
  3791. (if (null? nargs) ; (/ x 1 1) -> x
  3792. (car args)
  3793. (if (and (member (car args) (cdr args))
  3794. (not (side-effect? (car args) env)))
  3795. (let ((n (remove (car args) (cdr args))))
  3796. (if (null? (cdr n))
  3797. `(/ ,@n) ; (/ x y x) -> (/ y)
  3798. `(/ 1 ,@n))) ; (/ x y x z) -> (/ 1 y z)
  3799. `(/ ,@(cons (car args) nargs)))))))))
  3800. ((sin cos tan asin acos sinh cosh tanh asinh acosh atanh exp)
  3801. ;; perhaps someday, for amusement:
  3802. ;; (sin (acos x)) == (cos (asin x)) == (sqrt (- 1 (expt x 2)))
  3803. ;; (asin (cos x)) == (acos (sin x)) == (- (* 1/2 pi) x)
  3804. (cond ((not (= len 1))
  3805. `(,(car form) ,@args))
  3806. ((and (pair? (car args)) ; (sin (asin x)) -> x
  3807. (= (length (car args)) 2)
  3808. (eq? (caar args) (inverse-op (car form))))
  3809. (cadar args))
  3810. ((eqv? (car args) 0) ; (sin 0) -> 0
  3811. (case (car form)
  3812. ((sin asin sinh asinh tan tanh atanh) 0)
  3813. ((exp cos cosh) 1)
  3814. (else `(,(car form) ,@args))))
  3815. ((and (eq? (car form) 'cos) ; (cos (- x)) -> (cos x)
  3816. (pair? (car args))
  3817. (eq? (caar args) '-)
  3818. (null? (cddar args)))
  3819. `(cos ,(cadar args)))
  3820. ((or (eq? (car args) 'pi) ; (sin pi) -> 0.0
  3821. (and (pair? (car args))
  3822. (eq? (caar args) '-)
  3823. (eq? (cadar args) 'pi)
  3824. (null? (cddar args))))
  3825. (case (car form)
  3826. ((sin tan) 0.0)
  3827. ((cos) -1.0)
  3828. (else `(,(car form) ,@args))))
  3829. ((eqv? (car args) 0.0) ; (sin 0.0) -> 0.0
  3830. ((symbol->value (car form)) 0.0))
  3831. ((and (eq? (car form) 'acos) ; (acos -1) -> pi
  3832. (eqv? (car args) -1))
  3833. 'pi)
  3834. ((and (eq? (car form) 'exp) ; (exp (* a (log b))) -> (expt b a)
  3835. (pair? (car args))
  3836. (eq? (caar args) '*))
  3837. (let ((targ (cdar args)))
  3838. (cond ((not (= (length targ) 2))
  3839. `(,(car form) ,@args))
  3840. ((and (pair? (car targ))
  3841. (eq? (caar targ) 'log)
  3842. (pair? (cdar targ))
  3843. (null? (cddar targ)))
  3844. `(expt ,(cadar targ) ,(cadr targ)))
  3845. ((and (pair? (cadr targ))
  3846. (eq? (caadr targ) 'log)
  3847. (pair? (cdadr targ))
  3848. (null? (cddadr targ)))
  3849. `(expt ,(cadadr targ) ,(car targ)))
  3850. (else `(,(car form) ,@args)))))
  3851. (else `(,(car form) ,@args))))
  3852. ((log)
  3853. (cond ((not (pair? args)) form)
  3854. ((eqv? (car args) 1) 0) ; (log 1 ...) -> 0
  3855. ((and (= len 1) ; (log (exp x)) -> x
  3856. (pair? (car args))
  3857. (= (length (car args)) 2)
  3858. (eq? (caar args) 'exp))
  3859. (cadar args))
  3860. ((and (pair? (car args)) ; (log (sqrt x)) -> (* 1/2 (log x))
  3861. (eq? (caar args) 'sqrt))
  3862. `(* 1/2 (log ,(cadar args) ,@(cdr args))))
  3863. ((and (pair? (car args)) ; (log (expt x y)) -> (* y (log x))
  3864. (eq? (caar args) 'expt))
  3865. `(* ,(caddar args) (log ,(cadar args) ,@(cdr args))))
  3866. ((not (and (= len 2) ; (log x x) -> 1.0
  3867. (equal? (car args) (cadr args))))
  3868. `(log ,@args))
  3869. ((integer? (car args)) 1)
  3870. (else 1.0)))
  3871. ((sqrt)
  3872. (cond ((not (pair? args))
  3873. form)
  3874. ((and (rational? (car args))
  3875. (rational? (sqrt (car args)))
  3876. (= (car args) (sqrt (* (car args) (car args)))))
  3877. (sqrt (car args))) ; don't collapse (sqrt (* a a)), a=-1 for example, or -1-i -> 1+i whereas 1-i -> 1-i etc
  3878. ((and (pair? (car args))
  3879. (eq? (caar args) 'exp))
  3880. `(exp (/ ,(cadar args) 2))) ; (sqrt (exp x)) -> (exp (/ x 2))
  3881. (else `(sqrt ,@args))))
  3882. ((floor round ceiling truncate)
  3883. (cond ((not (= len 1))
  3884. form)
  3885. ((number? (car args))
  3886. (catch #t
  3887. (lambda () (apply (symbol->value (car form)) args))
  3888. (lambda any `(,(car form) ,@args))))
  3889. ((not (pair? (car args)))
  3890. `(,(car form) ,@args))
  3891. ((or (integer-result? (caar args))
  3892. (and (eq? (caar args) 'random)
  3893. (integer? (cadar args))))
  3894. (car args))
  3895. ((memq (caar args) '(inexact->exact exact))
  3896. `(,(car form) ,(cadar args)))
  3897. ((memq (caar args) '(* + / -)) ; maybe extend this list
  3898. `(,(car form) (,(caar args) ,@(map (lambda (p)
  3899. (if (and (pair? p)
  3900. (memq (car p) '(inexact->exact exact)))
  3901. (cadr p)
  3902. p))
  3903. (cdar args)))))
  3904. ((and (eq? (caar args) 'random)
  3905. (eq? (car form) 'floor)
  3906. (float? (cadar args))
  3907. (= (floor (cadar args)) (cadar args)))
  3908. `(random ,(floor (cadar args))))
  3909. (else `(,(car form) ,@args))))
  3910. ((abs magnitude)
  3911. (cond ((not (= len 1))
  3912. form)
  3913. ((and (pair? (car args)) ; (abs (abs x)) -> (abs x)
  3914. (hash-table-ref non-negative-ops (caar args)))
  3915. (car args))
  3916. ((rational? (car args))
  3917. (abs (car args)))
  3918. ((not (pair? (car args)))
  3919. `(,(car form) ,@args))
  3920. ((and (memq (caar args) '(modulo random))
  3921. (= (length (car args)) 3) ; (abs (modulo x 2)) -> (modulo x 2)
  3922. (real? (caddar args))
  3923. (positive? (caddar args)))
  3924. (car args))
  3925. ((and (eq? (caar args) '-) ; (abs (- x)) -> (abs x)
  3926. (pair? (cdar args))
  3927. (null? (cddar args)))
  3928. `(,(car form) ,(cadar args)))
  3929. (else `(,(car form) ,@args))))
  3930. ((imag-part)
  3931. (if (not (= len 1))
  3932. form
  3933. (if (or (real? (car args))
  3934. (and (pair? (car args))
  3935. (real-result? (caar args))))
  3936. 0.0
  3937. `(imag-part ,@args))))
  3938. ((real-part)
  3939. (if (not (= len 1))
  3940. form
  3941. (if (or (real? (car args))
  3942. (and (pair? (car args))
  3943. (real-result? (caar args))))
  3944. (car args)
  3945. `(real-part ,@args))))
  3946. ((denominator)
  3947. (if (not (= len 1))
  3948. form
  3949. (if (or (integer? (car args))
  3950. (and (pair? (car args))
  3951. (integer-result? (caar args))))
  3952. 1
  3953. `(denominator ,(car args)))))
  3954. ((numerator)
  3955. (cond ((not (= len 1))
  3956. form)
  3957. ((or (integer? (car args))
  3958. (and (pair? (car args))
  3959. (integer-result? (caar args))))
  3960. (car args))
  3961. ((rational? (car args))
  3962. (numerator (car args)))
  3963. (else `(numerator ,(car args)))))
  3964. ((random)
  3965. (cond ((not (and (= len 1)
  3966. (number? (car args))))
  3967. `(random ,@args))
  3968. ((eqv? (car args) 0)
  3969. 0)
  3970. ((morally-equal? (car args) 0.0)
  3971. 0.0)
  3972. (else `(random ,@args))))
  3973. ((complex make-rectangular)
  3974. (if (and (= len 2)
  3975. (morally-equal? (cadr args) 0.0)) ; morally so that 0 matches
  3976. (car args)
  3977. `(complex ,@args)))
  3978. ((make-polar)
  3979. (if (and (= len 2)
  3980. (morally-equal? (cadr args) 0.0))
  3981. (car args)
  3982. `(make-polar ,@args)))
  3983. ((rationalize lognot ash modulo remainder quotient)
  3984. (cond ((just-rationals? args)
  3985. (catch #t ; catch needed here for things like (ash 2 64)
  3986. (lambda ()
  3987. (apply (symbol->value (car form)) args))
  3988. (lambda ignore
  3989. `(,(car form) ,@args)))) ; use this form to pick up possible arg changes
  3990. ((and (eq? (car form) 'ash) ; (ash x 0) -> x
  3991. (= len 2) ; length of args
  3992. (eqv? (cadr args) 0))
  3993. (car args))
  3994. ((case (car form)
  3995. ((quotient) ; (quotient (remainder x y) y) -> 0
  3996. (and (= len 2)
  3997. (pair? (car args))
  3998. (eq? (caar args) 'remainder)
  3999. (= (length (car args)) 3)
  4000. (eqv? (caddar args) (cadr args))))
  4001. ((ash modulo) ; (modulo 0 x) -> 0
  4002. (and (= len 2) (eqv? (car args) 0)))
  4003. (else #f))
  4004. 0)
  4005. ((and (eq? (car form) 'modulo) ; (modulo (abs x) y) -> (modulo x y)
  4006. (= len 2)
  4007. (pair? (car args))
  4008. (eq? (caar args) 'abs))
  4009. `(modulo ,(cadar args) ,(cadr args)))
  4010. (else `(,(car form) ,@args))))
  4011. ((expt)
  4012. (cond ((not (= len 2))
  4013. form)
  4014. ((and (eqv? (car args) 0) ; (expt 0 x) -> 0
  4015. (not (eqv? (cadr args) 0)))
  4016. (if (and (integer? (cadr args))
  4017. (negative? (cadr args)))
  4018. (lint-format "attempt to divide by 0: ~A" 'expt (truncated-list->string form)))
  4019. 0)
  4020. ((or (and (eqv? (cadr args) 0) ; (expt x 0) -> 1
  4021. (not (eqv? (car args) 0)))
  4022. (eqv? (car args) 1)) ; (expt 1 x) -> 1
  4023. 1)
  4024. ((eqv? (cadr args) 1) ; (expt x 1) -> x
  4025. (car args))
  4026. ((eqv? (cadr args) -1) ; (expt x -1) -> (/ x)
  4027. `(/ ,(car args)))
  4028. ((just-rationals? args) ; (expt 2 3) -> 8
  4029. (catch #t
  4030. (lambda ()
  4031. (let ((val (apply expt args)))
  4032. (if (and (integer? val)
  4033. (< (abs val) 1000000))
  4034. val
  4035. `(expt ,@args))))
  4036. (lambda args
  4037. `(expt ,@args)))) ; (expt (expt x y) z) -> (expt x (* y z))
  4038. ((and (pair? (car args))
  4039. (eq? (caar args) 'expt))
  4040. `(expt ,(cadar args) (* ,(caddar args) ,(cadr args))))
  4041. (else `(expt ,@args))))
  4042. ((angle)
  4043. (cond ((not (pair? args)) form)
  4044. ((eqv? (car args) -1) 'pi)
  4045. ((or (morally-equal? (car args) 0.0)
  4046. (eq? (car args) 'pi))
  4047. 0.0)
  4048. (else `(angle ,@args))))
  4049. ((atan)
  4050. (cond ((and (= len 1) ; (atan (x y)) -> (atan x y)
  4051. (pair? (car args))
  4052. (= (length (car args)) 3)
  4053. (eq? (caar args) '/))
  4054. `(atan ,@(cdar args)))
  4055. ((and (= len 2) ; (atan 0 -1) -> pi
  4056. (eqv? (car args) 0)
  4057. (eqv? (cadr args) -1))
  4058. 'pi)
  4059. (else `(atan ,@args))))
  4060. ((inexact->exact exact)
  4061. (cond ((not (= len 1))
  4062. form)
  4063. ((or (rational? (car args))
  4064. (and (pair? (car args))
  4065. (or (rational-result? (caar args))
  4066. (integer-result? (caar args))
  4067. (and (eq? (caar args) 'random)
  4068. (rational? (cadar args))))))
  4069. (car args))
  4070. ((number? (car args))
  4071. (catch #t (lambda () (inexact->exact (car args))) (lambda any `(,(car form) ,@args))))
  4072. (else `(,(car form) ,@args))))
  4073. ((exact->inexact inexact)
  4074. (cond ((not (= len 1))
  4075. form)
  4076. ((memv (car args) '(0 0.0))
  4077. 0.0)
  4078. ((not (and (pair? (car args))
  4079. (not (eq? (caar args) 'random))
  4080. (hash-table-ref numeric-ops (caar args))
  4081. (any? number? (cdar args))))
  4082. `(,(car form) ,@args))
  4083. ((any? (lambda (x)
  4084. (and (number? x)
  4085. (inexact? x)))
  4086. (cdar args))
  4087. (car args))
  4088. (else
  4089. (let ((new-form (copy (car args))))
  4090. (do ((p (cdr new-form) (cdr p)))
  4091. ((or (null? p)
  4092. (number? (car p)))
  4093. (if (pair? p)
  4094. (set-car! p (* 1.0 (car p))))
  4095. new-form))))))
  4096. ;; not (inexact (random 3)) -> (random 3.0) because results are different
  4097. ((logior)
  4098. (set! args (lint-remove-duplicates (remove-all 0 (splice-if (lambda (x) (eq? x 'logior)) args)) env))
  4099. (if (every? (lambda (x) (or (not (number? x)) (integer? x))) args)
  4100. (let ((rats (collect-if list integer? args)))
  4101. (if (and (pair? rats)
  4102. (pair? (cdr rats)))
  4103. (let ((y (apply logior rats)))
  4104. (set! args (if (zero? y)
  4105. (collect-if list (lambda (x) (not (number? x))) args)
  4106. (cons y (collect-if list (lambda (x) (not (number? x))) args))))))))
  4107. (cond ((null? args) 0) ; (logior) -> 0
  4108. ((null? (cdr args)) (car args)) ; (logior x) -> x
  4109. ((memv -1 args) -1) ; (logior ... -1 ...) -> -1
  4110. ((just-integers? args) (apply logior args))
  4111. (else `(logior ,@args))))
  4112. ((logand)
  4113. (set! args (lint-remove-duplicates (remove-all -1 (splice-if (lambda (x) (eq? x 'logand)) args)) env))
  4114. (if (every? (lambda (x) (or (not (number? x)) (integer? x))) args)
  4115. (let ((rats (collect-if list integer? args)))
  4116. (if (and (pair? rats)
  4117. (pair? (cdr rats)))
  4118. (let ((y (apply logand rats)))
  4119. (set! args (if (= y -1)
  4120. (collect-if list (lambda (x) (not (number? x))) args)
  4121. (cons y (collect-if list (lambda (x) (not (number? x))) args))))))))
  4122. (cond ((null? args) -1)
  4123. ((null? (cdr args)) (car args)) ; (logand x) -> x
  4124. ((memv 0 args) 0)
  4125. ((just-integers? args) (apply logand args))
  4126. (else `(logand ,@args))))
  4127. ;; (logand 1 (logior 2 x)) -> (logand 1 x)?
  4128. ;; (logand 1 (logior 1 x)) -> 1
  4129. ;; (logand 3 (logior 1 x))?
  4130. ;; similarly for (logior...(logand...))
  4131. ((logxor)
  4132. (set! args (splice-if (lambda (x) (eq? x 'logxor)) args)) ; is this correct??
  4133. (cond ((null? args) 0) ; (logxor) -> 0
  4134. ((null? (cdr args)) (car args)) ; (logxor x) -> x??
  4135. ((just-integers? args) (apply logxor args)) ; (logxor 1 2) -> 3
  4136. ((and (= len 2) (equal? (car args) (cadr args))) 0) ; (logxor x x) -> 0
  4137. (else `(logxor ,@args)))) ; (logxor x (logxor y z)) -> (logxor x y z)
  4138. ((gcd)
  4139. (set! args (lint-remove-duplicates (splice-if (lambda (x) (eq? x 'gcd)) args) env))
  4140. (cond ((null? args) 0)
  4141. ((memv 1 args) 1)
  4142. ((just-rationals? args)
  4143. (catch #t ; maybe (gcd -9223372036854775808 -9223372036854775808)
  4144. (lambda ()
  4145. (apply gcd args))
  4146. (lambda ignore
  4147. `(gcd ,@args))))
  4148. ((null? (cdr args)) `(abs ,(car args)))
  4149. ((eqv? (car args) 0) `(abs ,(cadr args)))
  4150. ((eqv? (cadr args) 0) `(abs ,(car args)))
  4151. (else `(gcd ,@args))))
  4152. ((lcm)
  4153. (set! args (lint-remove-duplicates (splice-if (lambda (x) (eq? x 'lcm)) args) env))
  4154. (cond ((null? args) 1) ; (lcm) -> 1
  4155. ((memv 0 args) 0) ; (lcm ... 0 ...) -> 0
  4156. ((just-rationals? args) ; (lcm 3 4) -> 12
  4157. (catch #t
  4158. (lambda ()
  4159. (apply lcm args))
  4160. (lambda ignore
  4161. `(lcm ,@args))))
  4162. ((null? (cdr args)) ; (lcm x) -> (abs x)
  4163. `(abs ,(car args)))
  4164. (else `(lcm ,@args))))
  4165. ((max min)
  4166. (if (not (pair? args))
  4167. form
  4168. (begin
  4169. (set! args (lint-remove-duplicates (splice-if (lambda (x) (eq? x (car form))) args) env))
  4170. (if (any? (lambda (p) ; if non-negative-op, remove any non-positive numbers
  4171. (and (pair? p)
  4172. (hash-table-ref non-negative-ops (car p))))
  4173. args)
  4174. (set! args (remove-if (lambda (x)
  4175. (and (real? x)
  4176. (not (positive? x))))
  4177. args)))
  4178. (if (= len 1)
  4179. (car args)
  4180. (if (just-reals? args)
  4181. (apply (symbol->value (car form)) args)
  4182. (let ((nums (collect-if list number? args))
  4183. (other (if (eq? (car form) 'min) 'max 'min)))
  4184. (if (and (pair? nums)
  4185. (just-reals? nums)) ; non-real case checked elsewhere (later)
  4186. (let ((relop (if (eq? (car form) 'min) >= <=)))
  4187. (if (pair? (cdr nums))
  4188. (set! nums (list (apply (symbol->value (car form)) nums))))
  4189. (let ((new-args (append nums (collect-if list (lambda (x) (not (number? x))) args))))
  4190. (let ((c1 (car nums)))
  4191. (set! new-args (collect-if list (lambda (x)
  4192. (or (not (pair? x))
  4193. (<= (length x) 2)
  4194. (not (eq? (car x) other))
  4195. (let ((c2 (find-if number? (cdr x))))
  4196. (or (not c2)
  4197. (relop c1 c2)))))
  4198. new-args)))
  4199. (if (< (length new-args) (length args))
  4200. (set! args new-args)))))
  4201. ;; if (max c1 (min c2 . args1) . args2) where (> c1 c2) -> (max c1 . args2), if = -> c1
  4202. ;; if (min c1 (max c2 . args1) . args2) where (< c1 c2) -> (min c1 . args2), if = -> c1
  4203. ;; and if (max 4 x (min x 4)) -- is it (max x 4)?
  4204. ;; (max a b) is (- (min (- a) (- b))), but that doesn't help here -- the "-" gets in our way
  4205. ;; (min (- a) (- b)) -> (- (max a b))?
  4206. ;; (+ a (max|min b c)) = (max|min (+ a b) (+ a c)))
  4207. (if (null? (cdr args)) ; (max (min x 3) (min x 3)) -> (max (min x 3)) -> (min x 3)
  4208. (car args)
  4209. (if (and (null? (cddr args)) ; (max|min x (min|max x ...) -> x
  4210. (or (and (pair? (car args))
  4211. (eq? (caar args) other)
  4212. (member (cadr args) (car args))
  4213. (not (side-effect? (cadr args) env)))
  4214. (and (pair? (cadr args))
  4215. (eq? (caadr args) other)
  4216. (member (car args) (cadr args))
  4217. (not (side-effect? (car args) env)))))
  4218. ((if (pair? (car args)) cadr car) args)
  4219. `(,(car form) ,@args)))))))))
  4220. (else
  4221. `(,(car form) ,@args))))))
  4222. (define (binding-ok? caller head binding env second-pass)
  4223. ;; check let-style variable binding for various syntactic problems
  4224. (cond (second-pass
  4225. (and (pair? binding)
  4226. (symbol? (car binding))
  4227. (not (constant? (car binding)))
  4228. (pair? (cdr binding))
  4229. (or (null? (cddr binding))
  4230. (and (eq? head 'do)
  4231. (pair? (cddr binding)) ; (do ((i 0 . 1))...)
  4232. (null? (cdddr binding))))))
  4233. ((not (pair? binding)) (lint-format "~A binding is not a list? ~S" caller head binding) #f) ; (let (a) a)
  4234. ((not (symbol? (car binding))) (lint-format "~A variable is not a symbol? ~S" caller head binding) #f) ; (let ((1 2)) #f)
  4235. ((keyword? (car binding)) (lint-format "~A variable is a keyword? ~S" caller head binding) #f) ; (let ((:a 1)) :a)
  4236. ((constant? (car binding)) (lint-format "can't bind a constant: ~S" caller binding) #f) ; (let ((pi 2)) #f)
  4237. ((not (pair? (cdr binding)))
  4238. (lint-format (if (null? (cdr binding))
  4239. "~A variable value is missing? ~S" ; (let ((a)) #f)
  4240. "~A binding is an improper list? ~S") ; (let ((a . 1)) #f)
  4241. caller head binding)
  4242. #f)
  4243. ((and (pair? (cddr binding)) ; (let loop ((pi 1.0) (+ pi 1))...)
  4244. (or (not (eq? head 'do))
  4245. (pair? (cdddr binding))))
  4246. (lint-format "~A binding is messed up: ~A" caller head binding)
  4247. #f)
  4248. (else
  4249. (if (and (eq? caller (car binding))
  4250. (let ((fv (var-member caller env)))
  4251. (and (var? fv)
  4252. (memq (var-ftype fv) '(define lambda let define* lambda*)))))
  4253. (lint-format "~A variable ~A in ~S shadows the current function?" caller head caller binding)
  4254. (if (and *report-shadowed-variables* ; (let ((x 1)) (+ (let ((x 2)) (+ x 1)) x))
  4255. (var-member (car binding) env))
  4256. (lint-format "~A variable ~A in ~S shadows an earlier declaration" caller head (car binding) binding)))
  4257. #t)))
  4258. (define (check-char-cmp caller op form)
  4259. (if (and (any? (lambda (x)
  4260. (and (pair? x)
  4261. (eq? (car x) 'char->integer)))
  4262. (cdr form))
  4263. (every? (lambda (x)
  4264. (or (and (integer? x)
  4265. (<= 0 x 255))
  4266. (and (pair? x)
  4267. (eq? (car x) 'char->integer))))
  4268. (cdr form)))
  4269. (lint-format "perhaps ~A" caller ; (< (char->integer x) 95) -> (char<? x #\_)
  4270. (lists->string form
  4271. `(,(case op ((=) 'char=?) ((>) 'char>?) ((<) 'char<?) ((>=) 'char>=?) (else 'char<=?))
  4272. ,@(map (lambda (arg)
  4273. ((if (integer? arg) integer->char cadr) arg))
  4274. (cdr form)))))))
  4275. (define (write-port expr) ; ()=not specified (*stdout*), #f=something is wrong (not enough args)
  4276. (and (pair? expr)
  4277. (if (eq? (car expr) 'newline)
  4278. (if (pair? (cdr expr))
  4279. (cadr expr)
  4280. ())
  4281. (and (pair? (cdr expr))
  4282. (if (pair? (cddr expr))
  4283. (caddr expr)
  4284. ())))))
  4285. (define (display->format d)
  4286. (case (car d)
  4287. ((newline) (copy "~%"))
  4288. ((display)
  4289. (let* ((arg (cadr d))
  4290. (arg-arg (and (pair? arg)
  4291. (pair? (cdr arg))
  4292. (cadr arg))))
  4293. (cond ((string? arg)
  4294. arg)
  4295. ((char? arg)
  4296. (string arg))
  4297. ((and (pair? arg)
  4298. (eq? (car arg) 'number->string))
  4299. (if (= (length arg) 3)
  4300. (case (caddr arg)
  4301. ((2) (values "~B" arg-arg))
  4302. ((8) (values "~O" arg-arg))
  4303. ((10) (values "~D" arg-arg))
  4304. ((16) (values "~X" arg-arg))
  4305. (else (values "~A" arg)))
  4306. (values "~A" arg-arg)))
  4307. ((not (and (pair? arg)
  4308. (eq? (car arg) 'string-append)))
  4309. (values "~A" arg))
  4310. ((null? (cddr arg))
  4311. (if (string? arg-arg)
  4312. arg-arg
  4313. (values "~A" arg-arg)))
  4314. ((not (null? (cdddr arg)))
  4315. (values "~A" arg))
  4316. ((string? arg-arg)
  4317. (values (string-append arg-arg "~A") (caddr arg)))
  4318. ((string? (caddr arg))
  4319. (values (string-append "~A" (caddr arg)) arg-arg))
  4320. (else (values "~A" arg)))))
  4321. ((write)
  4322. ;; very few special cases actually happen here, unlike display above
  4323. (if (string? (cadr d))
  4324. (string-append "\"" (cadr d) "\"")
  4325. (if (char? (cadr d))
  4326. (string (cadr d))
  4327. (values "~S" (cadr d)))))
  4328. ((write-char)
  4329. (if (char? (cadr d))
  4330. (string (cadr d))
  4331. (values "~C" (cadr d))))
  4332. ((write-string) ; same as display but with possible start|end indices
  4333. (let ((indices (and (pair? (cddr d)) ; port
  4334. (pair? (cdddr d))
  4335. (cdddr d))))
  4336. (if (string? (cadr d))
  4337. (if (not indices)
  4338. (cadr d)
  4339. (if (and (integer? (car indices))
  4340. (or (null? (cdr indices))
  4341. (and (pair? indices)
  4342. (integer? (cadr indices)))))
  4343. (apply substring (cadr d) indices)
  4344. (values "~A" `(substring ,(cadr d) ,@indices))))
  4345. (values "~A" (if indices `(substring ,(cadr d) ,@indices) (cadr d))))))))
  4346. (define (identity? x) ; (lambda (x) x), or (define (x) x) -> procedure-source
  4347. (and (pair? x)
  4348. (eq? (car x) 'lambda)
  4349. (pair? (cdr x))
  4350. (pair? (cadr x))
  4351. (null? (cdadr x))
  4352. (pair? (cddr x))
  4353. (null? (cdddr x))
  4354. (eq? (caddr x) (caadr x))))
  4355. (define (cdr-count c)
  4356. (case c ((cdr) 1) ((cddr) 2) ((cdddr) 3) (else 4)))
  4357. (define (simple-lambda? x)
  4358. (and (pair? x)
  4359. (eq? (car x) 'lambda)
  4360. (pair? (cdr x))
  4361. (pair? (cadr x))
  4362. (null? (cdadr x))
  4363. (pair? (cddr x))
  4364. (null? (cdddr x))
  4365. (= (tree-count1 (caadr x) (caddr x) 0) 1)))
  4366. (define (less-simple-lambda? x)
  4367. (and (pair? x)
  4368. (eq? (car x) 'lambda)
  4369. (pair? (cdr x))
  4370. (pair? (cadr x))
  4371. (null? (cdadr x))
  4372. (pair? (cddr x))
  4373. (= (tree-count1 (caadr x) (cddr x) 0) 1)))
  4374. (define (tree-subst new old tree)
  4375. (cond ((equal? old tree)
  4376. new)
  4377. ((not (pair? tree))
  4378. tree)
  4379. ((eq? (car tree) 'quote)
  4380. (copy-tree tree))
  4381. (else (cons (tree-subst new old (car tree))
  4382. (tree-subst new old (cdr tree))))))
  4383. (define* (find-unique-name f1 f2 (i 1))
  4384. (let ((sym (string->symbol (format #f "_~D_" i))))
  4385. (if (not (or (eq? sym f1)
  4386. (eq? sym f2)
  4387. (tree-member sym f1)
  4388. (tree-member sym f2)))
  4389. sym
  4390. (find-unique-name f1 f2 (+ i 1)))))
  4391. (define (unrelop caller head form) ; assume len=3
  4392. (let ((arg1 (cadr form))
  4393. (arg2 (caddr form)))
  4394. (if (and (pair? arg1)
  4395. (= (length arg1) 3))
  4396. (if (eq? (car arg1) '-)
  4397. (if (memv arg2 '(0 0.0)) ; (< (- x y) 0) -> (< x y), need both 0 and 0.0 because (eqv? 0 0.0) is #f
  4398. (lint-format "perhaps ~A" caller
  4399. (lists->string form
  4400. `(,head ,(cadr arg1) ,(caddr arg1))))
  4401. (if (and (integer? arg2) ; (> (- x 50868) 256) -> (> x 51124)
  4402. (integer? (caddr arg1)))
  4403. (lint-format "perhaps ~A" caller
  4404. (lists->string form
  4405. `(,head ,(cadr arg1) ,(+ (caddr arg1) arg2))))))
  4406. ;; (> (- x) (- y)) (> (- x 1) (- y 1)) and so on -- do these ever happen? (no, not even if we allow +-*/)
  4407. (if (and (eq? (car arg1) '+) ; (< (+ x 1) 3) -> (< x 2)
  4408. (integer? arg2)
  4409. (integer? (caddr arg1)))
  4410. (lint-format "perhaps ~A" caller
  4411. (lists->string form
  4412. `(,head ,(cadr arg1) ,(- arg2 (caddr arg1)))))))
  4413. (if (and (pair? arg2)
  4414. (= (length arg2) 3))
  4415. (if (eq? (car arg2) '-)
  4416. (if (memv arg1 '(0 0.0)) ; (< 0 (- x y)) -> (> x y)
  4417. (lint-format "perhaps ~A" caller
  4418. (lists->string form
  4419. `(,(hash-table-ref reversibles head)
  4420. ,(cadr arg2) ,(caddr arg2))))
  4421. (if (and (integer? arg1)
  4422. (integer? (caddr arg2)))
  4423. (lint-format "perhaps ~A" caller
  4424. (lists->string form
  4425. `(,(hash-table-ref reversibles head)
  4426. ,(cadr arg2) ,(+ arg1 (caddr arg2)))))))
  4427. (if (and (eq? (car arg2) '+) ; (< 256 (+ fltdur 50868)) -> (> fltdur -50612)
  4428. (integer? arg1)
  4429. (integer? (caddr arg2)))
  4430. (lint-format "perhaps ~A" caller
  4431. (lists->string form
  4432. `(,(hash-table-ref reversibles head)
  4433. ,(cadr arg2) ,(- arg1 (caddr arg2)))))))))))
  4434. (define (check-start-and-end caller head form ff env)
  4435. (if (or (and (integer? (car form))
  4436. (integer? (cadr form))
  4437. (apply >= form))
  4438. (and (equal? (car form) (cadr form))
  4439. (not (side-effect? (car form) env))))
  4440. (lint-format "these ~A indices make no sense: ~A" caller head ff))) ; (copy x y 1 0)
  4441. (define (other-case c)
  4442. ((if (char-upper-case? c) char-downcase char-upcase) c))
  4443. (define (check-boolean-affinity caller form env)
  4444. ;; does built-in boolean func's arg make sense
  4445. (when (= (length form) 2)
  4446. (unless (or (and (symbol? (cadr form))
  4447. (not (keyword? (cadr form))))
  4448. (= line-number last-simplify-boolean-line-number))
  4449. (let ((expr (simplify-boolean form () () env)))
  4450. (if (not (equal? expr form))
  4451. (lint-format "perhaps ~A" caller (lists->string form expr)) ; (char? '#\a) -> #t
  4452. (if (code-constant? (cadr form))
  4453. (lint-format "perhaps ~A" caller (lists->string form (eval form)))))))
  4454. (if (and (symbol? (cadr form)) ; (number? pi) -> #t
  4455. (not (keyword? (cadr form)))
  4456. (not (var-member (cadr form) env)))
  4457. (let ((val (checked-eval form)))
  4458. (if (not (eq? val :checked-eval-error))
  4459. (lint-format "perhaps ~A" caller (lists->string form val)))))
  4460. (when (and (pair? (cadr form))
  4461. (symbol? (caadr form)))
  4462. (let ((rt (if (eq? (caadr form) 'quote)
  4463. (->simple-type (cadadr form))
  4464. (return-type (caadr form) env)))
  4465. (head (car form)))
  4466. (if (subsumes? head rt)
  4467. (lint-format "~A is always #t" caller (truncated-list->string form)) ; (char? '#\a) is always #t
  4468. (if (not (or (memq rt '(#t #f values))
  4469. (any-compatible? head rt)))
  4470. (lint-format "~A is always #f" caller (truncated-list->string form)))))))) ; (number? (make-list 1)) is always #f
  4471. (define combinable-cxrs (let ((h (make-hash-table)))
  4472. (for-each (lambda (c)
  4473. (hash-table-set! h c (let ((name (symbol->string c)))
  4474. (substring name 1 (- (length name) 1)))))
  4475. '(car cdr caar cadr cddr cdar caaar caadr caddr cdddr cdaar cddar cadar cdadr cadddr cddddr))
  4476. h))
  4477. ;; not combinable: caaaar caaadr caadar caaddr cadaar cadadr caddar cdaaar cdaadr cdadar cdaddr cddaar cddadr cdddar
  4478. (define (combine-cxrs form)
  4479. (let ((cxr? (lambda (s)
  4480. (and (pair? (cdr s))
  4481. (pair? (cadr s))
  4482. (memq (caadr s) '(car cdr cadr cddr cdar cdddr cddddr))))))
  4483. (and (cxr? form)
  4484. (let* ((arg1 (cadr form))
  4485. (arg2 (and arg1 (cxr? arg1) (cadr arg1)))
  4486. (arg3 (and arg2 (cxr? arg2) (cadr arg2))))
  4487. (values (string-append (hash-table-ref combinable-cxrs (car form))
  4488. (hash-table-ref combinable-cxrs (car arg1))
  4489. (if arg2 (hash-table-ref combinable-cxrs (car arg2)) "")
  4490. (if arg3 (hash-table-ref combinable-cxrs (car arg3)) ""))
  4491. (cadr (or arg3 arg2 arg1)))))))
  4492. #|
  4493. ;; this builds the lists below:
  4494. (let ((ci ())
  4495. (ic ()))
  4496. (for-each
  4497. (lambda (c)
  4498. (let ((name (reverse (substring (symbol->string c) 1 (- (length (symbol->string c)) 1)))))
  4499. (do ((sum 0)
  4500. (len (length name))
  4501. (i 0 (+ i 1))
  4502. (bit 0 (+ bit 2)))
  4503. ((= i len)
  4504. (set! ci (cons (cons c sum) ci))
  4505. (set! ic (cons (cons sum c) ic)))
  4506. (set! sum (+ sum (expt 2 (if (char=? (name i) #\a) bit (+ bit 1))))))))
  4507. '(car cdr caar cadr cddr cdar caaar caadr caddr cdddr cdaar cddar cadar cdadr cadddr cddddr
  4508. caaaar caaadr caadar caaddr cadaar cadadr caddar cdaaar cdaadr cdadar cdaddr cddaar cddadr cdddar))
  4509. (list (reverse ci) (reverse ic)))
  4510. |#
  4511. (define cxr->int (hash-table '(car . 1) '(cdr . 2)
  4512. '(caar . 5) '(cadr . 6) '(cddr . 10) '(cdar . 9)
  4513. '(caaar . 21) '(caadr . 22) '(caddr . 26) '(cdddr . 42) '(cdaar . 37) '(cddar . 41) '(cadar . 25) '(cdadr . 38)
  4514. '(cadddr . 106) '(cddddr . 170) '(caaaar . 85) '(caaadr . 86) '(caadar . 89) '(caaddr . 90) '(cadaar . 101) '(cadadr . 102)
  4515. '(caddar . 105) '(cdaaar . 149) '(cdaadr . 150) '(cdadar . 153) '(cdaddr . 154) '(cddaar . 165) '(cddadr . 166) '(cdddar . 169)))
  4516. (define int->cxr (hash-table '(1 . car) '(2 . cdr)
  4517. '(5 . caar) '(6 . cadr) '(10 . cddr) '(9 . cdar)
  4518. '(21 . caaar) '(22 . caadr) '(26 . caddr) '(42 . cdddr) '(37 . cdaar) '(41 . cddar) '(25 . cadar) '(38 . cdadr)
  4519. '(106 . cadddr) '(170 . cddddr) '(85 . caaaar) '(86 . caaadr) '(89 . caadar) '(90 . caaddr) '(101 . cadaar) '(102 . cadadr)
  4520. '(105 . caddar) '(149 . cdaaar) '(150 . cdaadr) '(153 . cdadar) '(154 . cdaddr) '(165 . cddaar) '(166 . cddadr) '(169 . cdddar)))
  4521. (define (match-cxr c1 c2)
  4522. (hash-table-ref int->cxr (logand (or (hash-table-ref cxr->int c1) 0)
  4523. (or (hash-table-ref cxr->int c2) 0))))
  4524. (define (mv-range producer env)
  4525. (if (symbol? producer)
  4526. (let ((v (var-member producer env)))
  4527. (and (var? v)
  4528. (pair? ((cdr v) 'values))
  4529. ((cdr v) 'values)))
  4530. (and (pair? producer)
  4531. (if (memq (car producer) '(lambda lambda*))
  4532. (count-values (cddr producer))
  4533. (if (eq? (car producer) 'values)
  4534. (let ((len (- (length producer) 1)))
  4535. (for-each
  4536. (lambda (p)
  4537. (if (and (pair? p) (eq? (car p) 'values))
  4538. (set! len (- (+ len (length p)) 2))))
  4539. (cdr producer))
  4540. (list len len))
  4541. (mv-range (car producer) env))))))
  4542. (define (eval-constant-expression caller form)
  4543. (if (every? code-constant? (cdr form))
  4544. (catch #t
  4545. (lambda ()
  4546. (let ((val (eval (copy form :readable))))
  4547. (lint-format "perhaps ~A" caller (lists->string form val)))) ; (eq? #(0) #(0)) -> #f
  4548. (lambda args
  4549. #t))))
  4550. (define (unbegin x)
  4551. ((if (and (pair? x)
  4552. (eq? (car x) 'begin))
  4553. cdr list)
  4554. x))
  4555. (define (un_{list} tree)
  4556. (if (not (pair? tree))
  4557. tree
  4558. (if (eq? (car tree) #_{list})
  4559. (if (assq #_{apply_values} (cdr tree))
  4560. (if (and (pair? (cadr tree))
  4561. (eq? (caadr tree) #_{apply_values}))
  4562. `(append ,(cadadr tree) ,(cadr (caddr tree)))
  4563. `(cons ,(cadr tree) ,(cadr (caddr tree))))
  4564. (cons 'list (un_{list} (cdr tree))))
  4565. (cons (if (eq? (car tree) #_{append})
  4566. 'append
  4567. (un_{list} (car tree)))
  4568. (un_{list} (cdr tree))))))
  4569. (define (qq-tree? tree)
  4570. (and (pair? tree)
  4571. (or (eq? (car tree) #_{apply_values})
  4572. (if (and (eq? (car tree) #_{list})
  4573. (assq #_{apply_values} (cdr tree)))
  4574. (or (not (= (length tree) 3))
  4575. (not (and (pair? (caddr tree))
  4576. (eq? (caaddr tree) #_{apply_values})))
  4577. (qq-tree? (cadr (caddr tree)))
  4578. (let ((applying (and (pair? (cadr tree))
  4579. (eq? (caadr tree) #_{apply_values}))))
  4580. (qq-tree? ((if applying cadadr cadr) tree))))
  4581. (or (qq-tree? (car tree))
  4582. (qq-tree? (cdr tree)))))))
  4583. (define special-case-functions
  4584. (let ((special-case-table (make-hash-table)))
  4585. (define (hash-special key value)
  4586. (if (hash-table-ref special-case-table key)
  4587. (format *stderr* "~A already has a value: ~A~%" key (hash-table-ref special-case-table key)))
  4588. (hash-table-set! special-case-table key value))
  4589. ;; ---------------- member and assoc ----------------
  4590. (let ()
  4591. (define (sp-memx caller head form env)
  4592. (define (list-one? p)
  4593. (and (pair? p)
  4594. (pair? (cdr p))
  4595. (null? (cddr p))
  4596. (case (car p)
  4597. ((list) cadr)
  4598. ((quote)
  4599. (and (pair? (cadr p))
  4600. (null? (cdadr p))
  4601. (if (symbol? (caadr p))
  4602. (lambda (x)
  4603. (list 'quote (caadr x)))
  4604. caadr)))
  4605. (else #f))))
  4606. (when (= (length form) 4)
  4607. (let ((func (list-ref form 3)))
  4608. (if (symbol? func)
  4609. (if (memq func '(eq? eqv? equal?)) ; (member x y eq?) -> (memq x y)
  4610. (let ((op (if (eq? head 'member) ; (member (car x) entries equal?) -> (member (car x) entries)
  4611. (case func ((eq?) 'memq) ((eqv?) 'memv) (else 'member))
  4612. (case func ((eq?) 'assq) ((eqv?) 'assv) (else 'assoc)))))
  4613. (lint-format "perhaps ~A" caller (lists->string form `(,op ,(cadr form) ,(caddr form)))))
  4614. (let ((sig (procedure-signature (symbol->value func)))) ; arg-signature here is too cranky
  4615. (if (and (pair? sig)
  4616. (not (eq? 'boolean? (car sig)))
  4617. (not (and (pair? (car sig))
  4618. (memq 'boolean? (car sig)))))
  4619. (lint-format "~A is a questionable ~A function" caller func head)))) ; (member 1 x abs)
  4620. ;; func not a symbol
  4621. (if (and (pair? func)
  4622. (= (length func) 3) ; (member 'a x (lambda (a b c) (eq? a b)))
  4623. (eq? (car func) 'lambda)
  4624. (pair? (cadr func))
  4625. (pair? (caddr func)))
  4626. (if (not (memv (length (cadr func)) '(2 -1)))
  4627. (lint-format "~A equality function (optional third arg) should take two arguments" caller head)
  4628. (if (eq? head 'member)
  4629. (let ((eq (caddr func))
  4630. (args (cadr func)))
  4631. (if (and (memq (car eq) '(eq? eqv? equal?))
  4632. (eq? (car args) (cadr eq))
  4633. (pair? (caddr eq))
  4634. (eq? (car (caddr eq)) 'car)
  4635. (pair? (cdr (caddr eq)))
  4636. (pair? (cdr args))
  4637. (eq? (cadr args) (cadr (caddr eq))))
  4638. (lint-format "member might perhaps be ~A" ; (member 'a x (lambda (a b) (eq? a (car b))))
  4639. caller
  4640. (if (or (eq? func 'eq?)
  4641. (eq? (car (caddr func)) 'eq?))
  4642. 'assq
  4643. (if (eq? (car (caddr func)) 'eqv?)
  4644. 'assv
  4645. 'assoc)))))))))))
  4646. (when (= (length form) 3)
  4647. (let ((selector (cadr form))
  4648. (items (caddr form)))
  4649. (let ((current-eqf (case head ((memq assq) 'eq?) ((memv assv) 'eqv?) (else 'equal?)))
  4650. (selector-eqf (car (eqf selector env)))
  4651. (one-item (and (memq head '(memq memv member)) (list-one? items))))
  4652. ;; one-item assoc doesn't simplify cleanly
  4653. (if one-item
  4654. (let* ((target (one-item items))
  4655. (iter-eqf (eqf target env)))
  4656. (if (or (symbol? target)
  4657. (and (pair? target)
  4658. (not (eq? (car target) 'quote))))
  4659. (set! target (list 'quote target))) ; ; (member x (list "asdf")) -> (string=? x "asdf") -- maybe equal? here?
  4660. (lint-format "perhaps ~A" caller (lists->string form `(,(cadr iter-eqf) ,selector ,target))))
  4661. ;; not one-item
  4662. (letrec ((duplicates? (lambda (lst fnc)
  4663. (and (pair? lst)
  4664. (or (fnc (car lst) (cdr lst))
  4665. (duplicates? (cdr lst) fnc)))))
  4666. (duplicate-constants? (lambda (lst fnc)
  4667. (and (pair? lst)
  4668. (or (and (constant? (car lst))
  4669. (fnc (car lst) (cdr lst)))
  4670. (duplicate-constants? (cdr lst) fnc))))))
  4671. (if (and (symbol? selector-eqf) ; (memq 1.0 x): perhaps memq -> memv
  4672. (not (eq? selector-eqf current-eqf)))
  4673. (lint-format "~A: perhaps ~A -> ~A" caller (truncated-list->string form) head
  4674. (if (memq head '(memq memv member))
  4675. (case selector-eqf ((eq?) 'memq) ((eqv?) 'memv) ((equal?) 'member))
  4676. (case selector-eqf ((eq?) 'assq) ((eqv?) 'assv) ((equal?) 'assoc)))))
  4677. ;; --------------------------------
  4678. ;; check for head mismatch with items
  4679. (when (pair? items)
  4680. (when (or (eq? (car items) 'list)
  4681. (and (eq? (car items) 'quote)
  4682. (pair? (cadr items))))
  4683. (let ((elements ((if (eq? (car items) 'quote) cadr cdr) items)))
  4684. (let ((baddy #f))
  4685. (catch #t
  4686. (lambda ()
  4687. (set! baddy ((if (eq? (car items) 'list) duplicate-constants? duplicates?)
  4688. elements (symbol->value head))))
  4689. (lambda args #f))
  4690. (if (pair? baddy) ; (member x (list "asd" "abc" "asd"))
  4691. (lint-format "duplicated entry ~S in ~A" caller (car baddy) items)))
  4692. (when (proper-list? elements)
  4693. (let ((maxf #f)
  4694. (keys (if (eq? (car items) 'quote)
  4695. (if (memq head '(memq memv member))
  4696. elements
  4697. (and (every? pair? elements)
  4698. (map car elements)))
  4699. (if (memq head '(memq memv member))
  4700. (and (every? code-constant? elements)
  4701. elements)
  4702. (and (every? (lambda (e)
  4703. (and (pair? e)
  4704. (eq? (car e) 'quote)))
  4705. elements)
  4706. (map caadr elements))))))
  4707. (when (proper-list? keys)
  4708. (if (eq? (car items) 'quote)
  4709. (do ((p keys (cdr p)))
  4710. ((or (null? p)
  4711. (memq maxf '(equal? #t))))
  4712. (let ((element (car p)))
  4713. (if (symbol? element)
  4714. (if (not maxf)
  4715. (set! maxf 'eq?))
  4716. (if (pair? element)
  4717. (begin
  4718. (if (and (eq? (car element) 'quote)
  4719. (pair? (cdr element)))
  4720. (lint-format "stray quote? ~A" caller form)) ; (memq x '(a 'b c))
  4721. (set! maxf #t))
  4722. (let ((type (if (symbol? element)
  4723. 'eq?
  4724. (car (->eqf (->simple-type element))))))
  4725. (if (or (memq maxf '(#f eq?))
  4726. (memq type '(#t equal?)))
  4727. (set! maxf type)))))))
  4728. ;; else (list ...)
  4729. (do ((p keys (cdr p)))
  4730. ((or (null? p)
  4731. (memq maxf '(equal? #t))))
  4732. (let ((element (car p)))
  4733. (if (symbol? element)
  4734. (set! maxf #t)
  4735. (let ((type (car (eqf element env))))
  4736. (if (or (memq maxf '(#f eq?))
  4737. (memq type '(#t equal?)))
  4738. (set! maxf type)))))))
  4739. (case maxf
  4740. ((eq?)
  4741. (if (not (memq head '(memq assq))) ; (member (car op) '(x y z))
  4742. (lint-format "~A could be ~A in ~A" caller
  4743. head
  4744. (if (memq head '(memv member)) 'memq 'assq)
  4745. form)))
  4746. ((eqv?)
  4747. (if (not (memq head '(memv assv))) ; (memq (strname 0) '(#\{ #\[ #\()))
  4748. (lint-format "~A ~Aould be ~A in ~A" caller
  4749. head
  4750. (if (memq head '(memq assq)) "sh" "c")
  4751. (if (memq head '(memq member)) 'memv 'assv)
  4752. form)))
  4753. ((equal? #t) ; (memq (car op) '("a" #()))
  4754. (if (not (memq head '(member assoc)))
  4755. (lint-format "~A should be ~A in ~A" caller
  4756. head
  4757. (if (memq head '(memq memv)) 'member 'assoc)
  4758. form)))))))
  4759. ;; --------------------------------
  4760. (if (and (= (length elements) 2) ; (memq expr '(#t #f))
  4761. (memq #t elements)
  4762. (memq #f elements))
  4763. (lint-format "perhaps ~A" caller (lists->string form `(boolean? ,selector))))))
  4764. ;; not (memv x '(0 0.0)) -> (zero? x) because x might not be a number
  4765. (case (car items)
  4766. ((map)
  4767. (let ((memx (memq head '(memq memv member))))
  4768. (when (and memx (= (length items) 3))
  4769. (let ((mapf (cadr items))
  4770. (map-items (caddr items)))
  4771. (cond ((eq? mapf 'car) ; (memq x (map car y)) -> (assq x y)
  4772. (lint-format "perhaps use assoc: ~A" caller
  4773. (lists->string form `(,(case current-eqf ((eq?) 'assq) ((eqv?) 'assv) ((equal?) 'assoc))
  4774. ,selector ,map-items))))
  4775. ((eq? selector #t)
  4776. (if (eq? mapf 'null?) ; (memq #t (map null? items)) -> (memq () items)
  4777. (lint-format "perhaps ~A" caller
  4778. (lists->string form `(memq () ,map-items)))
  4779. (let ((b (if (eq? mapf 'b) 'c 'b)))
  4780. ;; (memq #t (map cadr items)) -> (member #t items (lambda (a b) (cadr b)))
  4781. (lint-format "perhaps avoid 'map: ~A" caller
  4782. (lists->string form `(member #t ,map-items (lambda (a ,b) (,mapf ,b))))))))
  4783. ((and (pair? selector)
  4784. (eq? (car selector) 'string->symbol) ; this could be extended, but it doesn't happen
  4785. (eq? mapf 'string->symbol)
  4786. (not (and (pair? map-items)
  4787. (eq? (car map-items) 'quote))))
  4788. (lint-format "perhaps ~A" caller
  4789. ;; (memq (string->symbol x) (map string->symbol y)) -> (member x y string=?)
  4790. (lists->string form `(member ,(cadr selector) ,map-items string=?))))
  4791. (else
  4792. ;; (member x (map b items)) -> (member x items (lambda (a c) (equal? a (b c))))
  4793. (let ((b (if (eq? mapf 'b) 'c 'b))) ; a below can't collide because eqf won't return 'a
  4794. (lint-format "perhaps avoid 'map: ~A" caller
  4795. (lists->string form `(member ,selector ,map-items
  4796. (lambda (a ,b) (,current-eqf a (,mapf ,b)))))))))))))
  4797. ((string->list) ; (memv c (string->list s)) -> (char-position c s)
  4798. (lint-format "perhaps ~A" caller
  4799. (lists->string form `(char-position ,(cadr form) ,@(cdr items)))))
  4800. ((cons) ; (member x (cons y z)) -> (or (equal? x y) (member x z))
  4801. (if (not (pair? selector))
  4802. (lint-format "perhaps avoid 'cons: ~A" caller
  4803. (lists->string form `(or (,current-eqf ,selector ,(cadr items))
  4804. (,head ,selector ,(caddr items)))))))
  4805. ((append) ; (member x (append (list x) y)) -> (or (equal? x x) (member x y))
  4806. (if (and (not (pair? selector))
  4807. (= (length items) 3)
  4808. (pair? (cadr items))
  4809. (eq? (caadr items) 'list)
  4810. (null? (cddadr items)))
  4811. (lint-format "perhaps ~A" caller
  4812. (lists->string form `(or (,current-eqf ,selector ,(cadadr items))
  4813. (,head ,selector ,(caddr items))))))))))))
  4814. (when (and (memq head '(memq memv))
  4815. (pair? items)
  4816. (eq? (car items) 'quote)
  4817. (pair? (cadr items)))
  4818. (let ((nitems (length (cadr items))))
  4819. (if (pair? selector) ; (memv (string-ref x 0) '(+ -)) -> #f etc
  4820. (let ((sig (arg-signature (car selector) env)))
  4821. (if (and (pair? sig)
  4822. (symbol? (car sig))
  4823. (not (eq? (car sig) 'values)))
  4824. (let ((vals (map (lambda (item)
  4825. (if ((symbol->value (car sig)) item) item (values)))
  4826. (cadr items))))
  4827. (if (not (= (length vals) nitems))
  4828. (lint-format "perhaps ~A" caller
  4829. (lists->string form
  4830. (and (pair? vals)
  4831. `(,head ,selector ',vals)))))))))
  4832. (if (> nitems 20)
  4833. (lint-format "perhaps use a hash-table here, rather than ~A" caller (truncated-list->string form)))
  4834. (let ((bad (find-if (lambda (x)
  4835. (not (or (symbol? x)
  4836. (char? x)
  4837. (number? x)
  4838. (procedure? x) ; (memq abs '(1 #_abs 2)) !
  4839. (memq x '(#f #t () #<unspecified> #<undefined> #<eof>)))))
  4840. (cadr items))))
  4841. (if bad
  4842. (lint-format (if (and (pair? bad)
  4843. (eq? (car bad) 'unquote))
  4844. (values "stray comma? ~A" caller) ; (memq x '(a (unquote b) c))
  4845. (values "pointless list member: ~S in ~A" caller bad))
  4846. ;; quoted item here is caught above ; (memq x '(a (+ 1 2) 3))
  4847. form))))))))
  4848. (for-each (lambda (f)
  4849. (hash-special f sp-memx))
  4850. '(memq assq memv assv member assoc)))
  4851. ;; ---------------- car, cdr, etc ----------------
  4852. (let ()
  4853. (define (sp-crx caller head form env)
  4854. (if (not (= line-number last-simplify-cxr-line-number))
  4855. ((lambda* (cxr arg)
  4856. (when cxr
  4857. (set! last-simplify-cxr-line-number line-number)
  4858. (cond ((< (length cxr) 5) ; (car (cddr x)) -> (caddr x)
  4859. (lint-format "perhaps ~A" caller
  4860. (lists->string form `(,(symbol "c" cxr "r") ,arg))))
  4861. ;; if it's car|cdr followed by cdr's, use list-ref|tail
  4862. ((not (char-position #\a cxr)) ; (cddddr (cddr x)) -> (list-tail x 6)
  4863. (lint-format "perhaps ~A" caller (lists->string form `(list-tail ,arg ,(length cxr)))))
  4864. ((not (char-position #\a (substring cxr 1))) ; (car (cddddr (cddr x))) -> (list-ref x 6)
  4865. (lint-format "perhaps ~A" caller (lists->string form `(list-ref ,arg ,(- (length cxr) 1)))))
  4866. (else (set! last-simplify-cxr-line-number -1)))))
  4867. (combine-cxrs form)))
  4868. (when (pair? (cadr form))
  4869. (let ((arg (cadr form)))
  4870. (when (eq? head 'car)
  4871. (case (car arg)
  4872. ((list-tail) ; (car (list-tail x y)) -> (list-ref x y)
  4873. (lint-format "perhaps ~A" caller (lists->string form `(list-ref ,(cadr arg) ,(caddr arg)))))
  4874. ((memq memv member assq assv assoc)
  4875. (if (pair? (cdr arg)) ; (car (memq x ...)) is either x or (car #f) -> error
  4876. (lint-format "~A is ~A, or an error" caller (truncated-list->string form) (cadr arg))))))
  4877. (when (and (eq? (car arg) 'or) ; (cdr (or (assoc x y) (cons 1 2))) -> (cond ((assoc x y) => cdr) (else 2))
  4878. (not (eq? form last-rewritten-internal-define))
  4879. (= (length arg) 3))
  4880. (let ((arg1 (cadr arg))
  4881. (arg2 (caddr arg)))
  4882. (if (and (pair? arg2)
  4883. (or (and (memq (car arg2) '(cons list #_{list}))
  4884. (eq? head 'cdr))
  4885. (memq (car arg2) '(error throw))
  4886. (and (eq? (car arg2) 'quote)
  4887. (pair? (cdr arg2))
  4888. (pair? (cadr arg2)))))
  4889. (lint-format "perhaps ~A" caller
  4890. (lists->string form ; (cdr (or (assoc n oi) (list n y))) -> (cond ((assoc n oi) => cdr) (else (list y)))
  4891. `(cond (,arg1 => ,head)
  4892. (else ,(case (car arg2)
  4893. ((quote) ((symbol->value head) (cadr arg2)))
  4894. ((cons) (caddr arg2))
  4895. ((error throw) arg2)
  4896. (else `(list ,@(cddr arg2)))))))))))
  4897. (if (and (pair? arg) ; (cdr '(a)) -> ()
  4898. (eq? (car arg) 'quote)
  4899. (pair? (cdr arg))
  4900. (pair? (cadr arg))
  4901. (not (var-member head env)))
  4902. (let ((val (checked-eval form)))
  4903. (if (not (eq? val :checked-eval-error))
  4904. (lint-format "perhaps ~A -> ~A~A" caller
  4905. (object->string form)
  4906. (if (or (pair? val) (symbol? val)) "'" "")
  4907. (object->string val)))))
  4908. (if (and (memq head '(car cdr))
  4909. (eq? (car arg) 'cons))
  4910. (lint-format "(~A~A) is the same as ~A" ; (car (cons 1 2)) is the same as 1
  4911. caller head
  4912. (truncated-list->string arg)
  4913. (truncated-list->string ((if (eq? head 'car) cadr caddr) arg))))
  4914. (when (memq head '(car cadr caddr cadddr))
  4915. (if (memq (car arg) '(string->list vector->list)) ; (car (string->list x)) -> (string-ref x 0)
  4916. (lint-format "perhaps ~A" caller (lists->string form
  4917. `(,(if (eq? (car arg) 'string->list) 'string-ref 'vector-ref)
  4918. ,(cadr arg)
  4919. ,(case head ((car) 0) ((cadr) 1) ((caddr) 2) (else 3)))))
  4920. (if (memq (car arg) '(reverse reverse!))
  4921. (lint-format "perhaps ~A~A" caller ; (car (reverse x)) -> (list-ref x (- (length x) 1))
  4922. (if (eq? head 'car)
  4923. "use 'last from srfi-1, or "
  4924. "")
  4925. (lists->string form
  4926. (if (symbol? (cadr arg))
  4927. `(list-ref ,(cadr arg)
  4928. (- (length ,(cadr arg))
  4929. ,(case head ((car) 1) ((cadr) 2) ((caddr) 3) (else 4))))
  4930. `(let ((_1_ ,(cadr arg))) ; let is almost certainly cheaper than reverse
  4931. (list-ref _1_ (- (length _1_)
  4932. ,(case head ((car) 1) ((cadr) 2) ((caddr) 3) (else 4))))))))))))))
  4933. (for-each (lambda (f)
  4934. (hash-special (car f) sp-crx))
  4935. combinable-cxrs))
  4936. ;; not combinable cxrs:
  4937. ;; caaaar caaadr caadar caaddr cadaar cadadr caddar
  4938. ;; cdaaar cdaadr cdadar cdaddr cddaar cddadr cdddar
  4939. ;; ---------------- set-car! ----------------
  4940. (let ()
  4941. (define (sp-set-car! caller head form env)
  4942. (when (= (length form) 3)
  4943. (let ((target (cadr form)))
  4944. (if (pair? target)
  4945. (case (car target)
  4946. ((list-tail) ; (set-car! (list-tail x y) z) -> (list-set! x y z)
  4947. (lint-format "perhaps ~A" caller (lists->string form `(list-set! ,(cadr target) ,(caddr target) ,(caddr form)))))
  4948. ((cdr cddr cdddr cddddr) ; (set-car! (cddr (cdddr x)) y) -> (list-set! x 5 y)
  4949. (set! last-simplify-cxr-line-number line-number)
  4950. (lint-format "perhaps ~A" caller
  4951. (lists->string form
  4952. (if (and (pair? (cadr target))
  4953. (memq (caadr target) '(cdr cddr cdddr cddddr)))
  4954. ;; (set-car! (cdr (cddr x)) y) -> (list-set! x 3 y)
  4955. `(list-set! ,(cadadr target)
  4956. ,(+ (cdr-count (car target)) (cdr-count (caadr target)))
  4957. ,(caddr form))
  4958. ;; (set-car! (cdr x) y) -> (list-set! x 1 y)
  4959. `(list-set! ,(cadr target)
  4960. ,(cdr-count (car target))
  4961. ,(caddr form)))))))))))
  4962. (hash-special 'set-car! sp-set-car!))
  4963. ;; ---------------- not ----------------
  4964. (let ()
  4965. (define (sp-not caller head form env)
  4966. (if (and (pair? (cdr form))
  4967. (pair? (cadr form)))
  4968. (if (eq? (caadr form) 'not)
  4969. (let ((str (truncated-list->string (cadadr form)))) ; (not (not x)) -> (and x #t)
  4970. (lint-format "if you want a boolean, (not (not ~A)) -> (and ~A #t)" 'paranoia str str))
  4971. (let ((sig (arg-signature (caadr form) env)))
  4972. (if (and (pair? sig)
  4973. (if (pair? (car sig)) ; (not (+ x y))
  4974. (not (memq 'boolean? (car sig)))
  4975. (not (memq (car sig) '(#t values boolean?)))))
  4976. (lint-format "~A can't be true (~A never returns #f)" caller (truncated-list->string form) (caadr form))))))
  4977. (if (not (= line-number last-simplify-boolean-line-number))
  4978. (let ((val (simplify-boolean form () () env)))
  4979. (set! last-simplify-boolean-line-number line-number)
  4980. (if (not (equal? form val)) ; (not (and (> x 2) (not z))) -> (or (<= x 2) z)
  4981. (lint-format "perhaps ~A" caller (lists->string form val))))))
  4982. (hash-special 'not sp-not))
  4983. ;; ---------------- and/or ----------------
  4984. (let ()
  4985. (define (sp-and caller head form env)
  4986. (if (not (= line-number last-simplify-boolean-line-number))
  4987. (let ((val (simplify-boolean form () () env)))
  4988. (set! last-simplify-boolean-line-number line-number)
  4989. (if (not (equal? form val)) ; (and (not x) (not y)) -> (not (or x y))
  4990. (lint-format "perhaps ~A" caller (lists->string form val)))))
  4991. (if (pair? (cdr form))
  4992. (do ((p (cdr form) (cdr p)))
  4993. ((null? (cdr p)))
  4994. (if (and (pair? (car p))
  4995. (eq? (caar p) 'if)
  4996. (= (length (car p)) 3)) ; (and (member n cvars) (if (pair? open) (not (member n open))) (not (eq? n open)))
  4997. (lint-format "one-armed if might cause confusion here: ~A" caller form)))))
  4998. (hash-special 'and sp-and)
  4999. (hash-special 'or sp-and))
  5000. ;; ---------------- = ----------------
  5001. (let ()
  5002. (define (sp-= caller head form env)
  5003. (let ((len (length form)))
  5004. (if (and (> len 2)
  5005. (let any-real? ((lst (cdr form))) ; ignore 0.0 and 1.0 in this since they normally work
  5006. (and (pair? lst)
  5007. (or (and (number? (car lst))
  5008. (not (rational? (car lst)))
  5009. (not (member (car lst) '(0.0 1.0) =)))
  5010. (any-real? (cdr lst)))))) ; (= x 1.5)
  5011. (lint-format "= can be troublesome with floats: ~A" caller (truncated-list->string form)))
  5012. (let ((cleared-form (cons = (remove-if (lambda (x) (not (number? x))) (cdr form)))))
  5013. (if (and (> (length cleared-form) 2)
  5014. (not (checked-eval cleared-form))) ; (= 1 y 2)
  5015. (lint-format "this comparison can't be true: ~A" caller (truncated-list->string form))))
  5016. (when (= len 3)
  5017. (let ((arg1 (cadr form))
  5018. (arg2 (caddr form)))
  5019. ;; (= (+ x a) (+ y a)) and various equivalents happen very rarely (only in test suites it appears)
  5020. (let ((var (or (and (memv arg1 '(0 1))
  5021. (pair? arg2)
  5022. (eq? (car arg2) 'length)
  5023. (cadr arg2))
  5024. (and (memv arg2 '(0 1))
  5025. (pair? arg1)
  5026. (eq? (car arg1) 'length)
  5027. (cadr arg1)))))
  5028. ;; we never seem to have var-member/initial-value/history here to distinguish types
  5029. ;; and a serious attempt to do so was a bust.
  5030. (if var
  5031. (if (or (eqv? arg1 0) ; (= (length x) 0) -> (null? x)
  5032. (eqv? arg2 0))
  5033. (lint-format "perhaps (assuming ~A is a list), ~A" caller var
  5034. (lists->string form `(null? ,var)))
  5035. (if (symbol? var) ; (= (length x) 1) -> (and (pair? x) (null? (cdr x)))
  5036. (lint-format "perhaps (assuming ~A is a list), ~A" caller var
  5037. (lists->string form `(and (pair? ,var) (null? (cdr ,var))))))))))
  5038. (unrelop caller '= form))
  5039. (check-char-cmp caller '= form)))
  5040. (hash-special '= sp-=))
  5041. ;; ---------------- < > <= >= ----------------
  5042. (let ()
  5043. (define (sp-< caller head form env)
  5044. (let ((cleared-form (cons head ; keep operator
  5045. (remove-if (lambda (x)
  5046. (not (number? x)))
  5047. (cdr form)))))
  5048. (if (and (> (length cleared-form) 2)
  5049. (not (checked-eval cleared-form))) ; (< x 1 2 0 y)
  5050. (lint-format "this comparison can't be true: ~A" caller (truncated-list->string form))))
  5051. (if (= (length form) 3)
  5052. (unrelop caller head form)
  5053. (when (> (length form) 3)
  5054. (if (and (memq head '(< >)) ; (< x y x) -> #f
  5055. (repeated-member? (cdr form) env))
  5056. (lint-format "perhaps ~A" caller (truncated-lists->string form #f))
  5057. (if (and (memq head '(<= >=))
  5058. (repeated-member? (cdr form) env))
  5059. (do ((last-arg (cadr form))
  5060. (new-args (list (cadr form)))
  5061. (lst (cddr form) (cdr lst)))
  5062. ((null? lst)
  5063. (if (repeated-member? new-args env) ; (<= x y x z x) -> (= x y z)
  5064. (lint-format "perhaps ~A" caller (truncated-lists->string form `(= ,@(lint-remove-duplicates (reverse new-args) env))))
  5065. (if (< (length new-args) (length (cdr form)))
  5066. (lint-format "perhaps ~A" caller ; (<= x x y z) -> (= x y z)
  5067. (truncated-lists->string form (or (null? (cdr new-args))
  5068. `(= ,@(reverse new-args))))))))
  5069. (unless (equal? (car lst) last-arg)
  5070. (set! last-arg (car lst))
  5071. (set! new-args (cons last-arg new-args))))))))
  5072. (cond ((not (= (length form) 3)))
  5073. ((and (real? (cadr form))
  5074. (or (< (cadr form) 0)
  5075. (and (zero? (cadr form))
  5076. (eq? head '>)))
  5077. (pair? (caddr form)) ; (> 0 (string-length x))
  5078. (hash-table-ref non-negative-ops (caaddr form)))
  5079. (lint-format "~A can't be negative: ~A" caller (caaddr form) (truncated-list->string form)))
  5080. ((and (real? (caddr form))
  5081. (or (< (caddr form) 0)
  5082. (and (zero? (caddr form))
  5083. (eq? head '<)))
  5084. (pair? (cadr form)) ; (< (string-length x) 0)
  5085. (hash-table-ref non-negative-ops (caadr form)))
  5086. (lint-format "~A can't be negative: ~A" caller (caadr form) (truncated-list->string form)))
  5087. ((and (pair? (cadr form))
  5088. (eq? (caadr form) 'length))
  5089. (let ((arg (cadadr form)))
  5090. (when (symbol? arg) ; (>= (length x) 0) -> (list? x)
  5091. ;; see comment above about distinguishing types! (twice I've wasted my time)
  5092. (if (eqv? (caddr form) 0)
  5093. (lint-format "perhaps~A ~A" caller
  5094. (if (eq? head '<) "" (format #f " (assuming ~A is a proper list)," arg))
  5095. (lists->string form
  5096. (case head
  5097. ((<) `(and (pair? ,arg) (not (proper-list? ,arg))))
  5098. ((<=) `(null? ,arg))
  5099. ((>) `(pair? ,arg))
  5100. ((>=) `(list? ,arg)))))
  5101. (if (and (eqv? (caddr form) 1)
  5102. (not (eq? head '>))) ; (<= (length x) 1) -> (or (null? x) (null? (cdr x)))
  5103. (lint-format "perhaps (assuming ~A is a proper list), ~A" caller arg
  5104. (lists->string form
  5105. (case head
  5106. ((<) `(null? ,arg))
  5107. ((<=) `(or (null? ,arg) (null? (cdr ,arg))))
  5108. ((>) `(and (pair? ,arg) (pair? (cdr ,arg))))
  5109. ((>=) `(pair? ,arg))))))))))
  5110. ((and (pair? (caddr form))
  5111. (eq? (caaddr form) 'length))
  5112. (let ((arg (cadr (caddr form))))
  5113. (when (symbol? arg) ; (>= 0 (length x)) -> (null? x)
  5114. (if (eqv? (cadr form) 0)
  5115. (lint-format "perhaps~A ~A" caller
  5116. (if (eq? head '>) "" (format #f " (assuming ~A is a proper list)," arg))
  5117. (lists->string form
  5118. (case head
  5119. ((<) `(pair? ,arg))
  5120. ((<=) `(list? ,arg))
  5121. ((>) `(and (pair? ,arg) (not (proper-list? ,arg))))
  5122. ((>=) `(null? ,arg)))))
  5123. (if (and (eqv? (cadr form) 1)
  5124. (not (eq? head '<))) ; (> 1 (length x)) -> (null? x)
  5125. (lint-format "perhaps (assuming ~A is a proper list), ~A" caller arg
  5126. (lists->string form
  5127. (case head
  5128. ((<) `(and (pair? ,arg) (pair? (cdr ,arg))))
  5129. ((<=) `(pair? ,arg))
  5130. ((>) `(null? ,arg))
  5131. ((>=) `(or (null? ,arg) (null? (cdr ,arg))))))))))))
  5132. ((and (eq? head '<)
  5133. (eqv? (caddr form) 1)
  5134. (pair? (cadr form)) ; (< (vector-length x) 1) -> (equal? x #())
  5135. (memq (caadr form) '(string-length vector-length)))
  5136. (lint-format "perhaps ~A" caller (lists->string form `(,(if (eq? (caadr form) 'string-length) 'string=? 'equal?)
  5137. ,(cadadr form)
  5138. ,(if (eq? (caadr form) 'string-length) "" #())))))
  5139. ((and (eq? head '>)
  5140. (eqv? (cadr form) 1)
  5141. (pair? (caddr form)) ; (> 1 (string-length x)) -> (string=? x "")
  5142. (memq (caaddr form) '(string-length vector-length)))
  5143. (lint-format "perhaps ~A" caller (lists->string form `(,(if (eq? (caaddr form) 'string-length) 'string=? 'equal?)
  5144. ,(cadr (caddr form))
  5145. ,(if (eq? (caaddr form) 'string-length) "" #())))))
  5146. ((and (memq head '(<= >=))
  5147. (or (and (eqv? (caddr form) 0)
  5148. (pair? (cadr form)) ; (<= (string-length m) 0) -> (= (string-length m) 0)
  5149. (hash-table-ref non-negative-ops (caadr form)))
  5150. (and (eqv? (cadr form) 0)
  5151. (pair? (caddr form))
  5152. (hash-table-ref non-negative-ops (caaddr form)))))
  5153. (lint-format "~A is never negative, so ~A" caller
  5154. ((if (eqv? (caddr form) 0) caadr caaddr) form)
  5155. (lists->string form (or (not (eq? (eq? head '<=)
  5156. (eqv? (caddr form) 0)))
  5157. `(= ,@(cdr form))))))
  5158. ((and (eqv? (caddr form) 256)
  5159. (pair? (cadr form)) ; (< (char->integer key) 256) -> #t
  5160. (eq? (caadr form) 'char->integer))
  5161. (lint-format "perhaps ~A" caller
  5162. (lists->string form (and (memq head '(< <=)) #t))))
  5163. ((or (and (eqv? (cadr form) 0) ; (> (numerator x) 0) -> (> x 0)
  5164. (pair? (caddr form))
  5165. (eq? (caaddr form) 'numerator))
  5166. (and (eqv? (caddr form) 0)
  5167. (pair? (cadr form))
  5168. (eq? (caadr form) 'numerator)))
  5169. (lint-format "perhaps ~A" caller
  5170. (lists->string form
  5171. (if (eqv? (cadr form) 0)
  5172. `(,head ,(cadr form) ,(cadr (caddr form)))
  5173. `(,head ,(cadadr form) ,(caddr form)))))))
  5174. (check-char-cmp caller head form))
  5175. ;; could change (> x 0) to (positive? x) and so on, but the former is clear and ubiquitous
  5176. (for-each (lambda (f)
  5177. (hash-special f sp-<))
  5178. '(< > <= >=))) ; '= handled above
  5179. ;; ---------------- char< char> etc ----------------
  5180. (let ()
  5181. (define (sp-char< caller head form env)
  5182. ;; only once: (char<=? #\0 c #\1)
  5183. (let ((cleared-form (cons head ; keep operator
  5184. (remove-if (lambda (x)
  5185. (not (char? x)))
  5186. (cdr form)))))
  5187. (if (and (> (length cleared-form) 2) ; (char>? x #\a #\b y)
  5188. (not (checked-eval cleared-form)))
  5189. (lint-format "this comparison can't be true: ~A" caller (truncated-list->string form))))
  5190. (if (and (eq? head 'char-ci=?) ; (char-ci=? x #\return)
  5191. (pair? (cdr form))
  5192. (pair? (cddr form))
  5193. (null? (cdddr form)) ; (char-ci=? x #\return)
  5194. (or (and (char? (cadr form))
  5195. (char=? (cadr form) (other-case (cadr form))))
  5196. (and (char? (caddr form))
  5197. (char=? (caddr form) (other-case (caddr form))))))
  5198. (lint-format "char-ci=? could be char=? here: ~A" caller form)
  5199. (when (and (eq? head 'char=?) ; (char=? #\a (char-downcase x)) -> (char-ci=? #\a x)
  5200. (let ((casef (let ((op #f))
  5201. (lambda (a)
  5202. (or (char? a)
  5203. (and (pair? a)
  5204. (memq (car a) '(char-downcase char-upcase))
  5205. (if op
  5206. (eq? op (car a))
  5207. (set! op (car a)))))))))
  5208. (every? casef (cdr form))))
  5209. (lint-format "perhaps ~A" caller
  5210. (lists->string form ; (char=? #\a (char-downcase x)) -> (char-ci=? #\a x)
  5211. `(char-ci=? ,@(map (lambda (a)
  5212. (if (and (pair? a)
  5213. (memq (car a) '(char-upcase char-downcase)))
  5214. (cadr a)
  5215. a))
  5216. (cdr form))))))))
  5217. (for-each (lambda (f)
  5218. (hash-special f sp-char<))
  5219. '(char<? char>? char<=? char>=? char=? char-ci<? char-ci>? char-ci<=? char-ci>=? char-ci=?)))
  5220. ;; ---------------- string< string> etc ----------------
  5221. (let ()
  5222. (define (sp-string< caller head form env)
  5223. (let ((cleared-form (cons head ; keep operator
  5224. (remove-if (lambda (x)
  5225. (not (string? x)))
  5226. (cdr form)))))
  5227. (if (and (> (length cleared-form) 2) ; (string>? "a" x "b" y)
  5228. (not (checked-eval cleared-form)))
  5229. (lint-format "this comparison can't be true: ~A" caller (truncated-list->string form))))
  5230. (if (and (> (length form) 2)
  5231. (let ((casef (let ((op #f)) ; (string=? x (string-downcase y)) -> (string-ci=? x y)
  5232. (lambda (a)
  5233. (and (pair? a)
  5234. (memq (car a) '(string-downcase string-upcase))
  5235. (if op
  5236. (eq? op (car a))
  5237. (set! op (car a))))))))
  5238. (every? casef (cdr form))))
  5239. (lint-format "perhaps ~A" caller ; (string=? (string-downcase x) (string-downcase y)) -> (string-ci=? x y)
  5240. (lists->string form
  5241. (let ((op (case head
  5242. ((string=?) 'string-ci=?)
  5243. ((string<=?) 'string-ci<=?)
  5244. ((string>=?) 'string-ci>=?)
  5245. ((string<?) 'string-ci<?)
  5246. ((string>?) 'string-ci>?)
  5247. (else head))))
  5248. `(,op ,@(map (lambda (a)
  5249. (if (and (pair? a)
  5250. (memq (car a) '(string-upcase string-downcase)))
  5251. (cadr a)
  5252. a))
  5253. (cdr form)))))))
  5254. (if (any? (lambda (a) ; string-copy is redundant in arg list
  5255. (and (pair? a)
  5256. (memq (car a) '(copy string-copy))
  5257. (null? (cddr a))))
  5258. (cdr form))
  5259. (let cleaner ((args (cdr form)) (new-args ())) ; (string=? "" (string-copy "")) -> (string=? "" "")
  5260. (if (not (pair? args))
  5261. (lint-format "perhaps ~A" caller (lists->string form `(,head ,@(reverse new-args))))
  5262. (let ((a (car args)))
  5263. (cleaner (cdr args)
  5264. (cons (if (and (pair? a)
  5265. (memq (car a) '(copy string-copy))
  5266. (null? (cddr a)))
  5267. (cadr a)
  5268. a)
  5269. new-args))))))
  5270. (when (and (eq? head 'string=?)
  5271. (= (length form) 3)) ; (string=? (symbol->string a) (symbol->string b)) -> (eq? a b)
  5272. (if (and (pair? (cadr form))
  5273. (eq? (caadr form) 'symbol->string)
  5274. (pair? (caddr form))
  5275. (eq? (caaddr form) 'symbol->string))
  5276. (lint-format "perhaps ~A" caller (lists->string form `(eq? ,(cadadr form) ,(cadr (caddr form)))))
  5277. (let ((s1 #f)
  5278. (s2 #f))
  5279. (if (and (string? (cadr form))
  5280. (= (length (cadr form)) 1))
  5281. (begin
  5282. (set! s1 (cadr form))
  5283. (set! s2 (caddr form)))
  5284. (if (and (string? (caddr form))
  5285. (= (length (caddr form)) 1))
  5286. (begin
  5287. (set! s1 (caddr form))
  5288. (set! s2 (cadr form)))))
  5289. (if (and s1 ; (string=? (substring r 0 1) "S")
  5290. (pair? s2)
  5291. (eq? (car s2) 'substring)
  5292. (= (length s2) 4)
  5293. (eqv? (list-ref s2 2) 0)
  5294. (eqv? (list-ref s2 3) 1))
  5295. (lint-format "perhaps ~A" caller
  5296. (lists->string form `(char=? (string-ref ,(cadr s2) 0) ,(string-ref s1 0))))))))
  5297. (if (every? (lambda (a) ; (string=? "#" (string (string-ref s 0))) -> (char=? #\# (string-ref s 0))
  5298. (or (and (string? a)
  5299. (= (length a) 1))
  5300. (and (pair? a)
  5301. (eq? (car a) 'string))))
  5302. (cdr form))
  5303. (lint-format "perhaps ~A" caller
  5304. (lists->string form
  5305. `(,(symbol "char" (substring (symbol->string head) 6))
  5306. ,@(map (lambda (a)
  5307. (if (string? a)
  5308. (string-ref a 0)
  5309. (cadr a)))
  5310. (cdr form)))))))
  5311. (for-each (lambda (f)
  5312. (hash-special f sp-string<))
  5313. '(string<? string>? string<=? string>=? string=? string-ci<? string-ci>? string-ci<=? string-ci>=? string-ci=?)))
  5314. ;; ---------------- length ----------------
  5315. (let ()
  5316. (define (sp-length caller head form env)
  5317. (when (pair? (cdr form))
  5318. (if (pair? (cadr form))
  5319. (let ((arg (cadr form))
  5320. (arg-args (cdadr form)))
  5321. (case (car arg)
  5322. ((string->list vector->list)
  5323. (if (null? (cdr arg-args)) ; string->list has start:end etc ; (length (string->list x)) -> (length x)
  5324. (lint-format "perhaps ~A" caller (lists->string form `(length ,(car arg-args))))
  5325. (if (pair? (cddr arg-args))
  5326. (if (and (integer? (caddr arg-args)) ; (length (vector->list x 1)) -> (- (length x) 1)
  5327. (integer? (cadr arg-args)))
  5328. (lint-format "perhaps ~A -> ~A" caller (truncated-list->string form) (max 0 (- (caddr arg-args) (cadr arg-args))))
  5329. (lint-format "perhaps ~A" caller (lists->string form `(- ,(caddr arg-args) ,(cadr arg-args)))))
  5330. (lint-format "perhaps ~A" caller (lists->string form `(- (length ,(car arg-args)) ,(cadr arg-args)))))))
  5331. ((reverse reverse! list->vector list->string let->list)
  5332. (lint-format "perhaps ~A" caller (lists->string form `(length ,(car arg-args)))))
  5333. ((cons) ; (length (cons item items)) -> (+ (length items) 1)
  5334. (lint-format "perhaps ~A" caller (lists->string form `(+ (length ,(cadr arg-args)) 1))))
  5335. ((make-list) ; (length (make-list 3)) -> 3
  5336. (lint-format "perhaps ~A" caller (lists->string form (car arg-args))))
  5337. ((list) ; (length (list 'a 'b 'c)) -> 3
  5338. (lint-format "perhaps ~A" caller (lists->string form (- (length arg) 1))))
  5339. ((append) ; (length (append x y)) -> (+ (length x) (length y))
  5340. (if (= (length arg) 3)
  5341. (lint-format "perhaps ~A" caller (lists->string form `(+ (length ,(car arg-args)) (length ,(cadr arg-args)))))))
  5342. ((quote) ; (length '(1 2 3)) -> 3
  5343. (if (list? (car arg-args))
  5344. (lint-format "perhaps ~A" caller (lists->string form (length (car arg-args))))))))
  5345. ;; not pair cadr
  5346. (if (code-constant? (cadr form)) ; (length 0) -> #f
  5347. (lint-format "perhaps ~A -> ~A" caller
  5348. (truncated-list->string form)
  5349. (length ((if (and (pair? (cadr form))
  5350. (eq? (caadr form) 'quote))
  5351. cadadr cadr)
  5352. form)))))))
  5353. (hash-special 'length sp-length))
  5354. ;; ---------------- zero? positive? negative? ----------------
  5355. (let ()
  5356. (define (sp-zero? caller head form env)
  5357. (when (pair? (cdr form))
  5358. (let ((arg (cadr form)))
  5359. (if (and (real? arg) ; (zero? 0) -> #t
  5360. (null? (cddr form))
  5361. (not (var-member head env)))
  5362. (lint-format "perhaps ~A" caller (lists->string form (eval form))))
  5363. (when (pair? arg)
  5364. (if (and (eq? head 'negative?) ; (negative? (string-length s))")
  5365. (hash-table-ref non-negative-ops (car arg)))
  5366. (lint-format "~A can't be negative: ~A" caller (caadr form) (truncated-list->string form)))
  5367. (case (car arg)
  5368. ((-)
  5369. (lint-format "perhaps ~A" caller ; (zero? (- x)) -> (zero? x)
  5370. (lists->string form
  5371. (let ((op '((zero? = zero?) (positive? > negative?) (negative? < positive?))))
  5372. (if (null? (cddr arg))
  5373. `(,(caddr (assq head op)) ,(cadr arg))
  5374. (if (null? (cdddr arg))
  5375. `(,(cadr (assq head op)) ,(cadr arg) ,(caddr arg))
  5376. `(,(cadr (assq head op)) ,(cadr arg) (+ ,@(cddr arg)))))))))
  5377. ((numerator) ; (negative? (numerator x)) -> (negative? x)
  5378. (lint-format "perhaps ~A" caller (lists->string form `(,head ,(cadadr form)))))
  5379. ((denominator) ; (zero? (denominator x)) -> error
  5380. (if (eq? head 'zero)
  5381. (lint-format "denominator can't be zero: ~A" caller form)))
  5382. ((string-length) ; (zero? (string-length x)) -> (string=? x "")
  5383. (if (eq? head 'zero?)
  5384. (lint-format "perhaps ~A" caller (lists->string form `(string=? ,(cadadr form) "")))))
  5385. ((vector-length) ; (zero? (vector-length c)) -> (equal? c #())
  5386. (if (eq? head 'zero?)
  5387. (lint-format "perhaps ~A" caller (lists->string form `(equal? ,(cadadr form) #())))))
  5388. ((length) ; (zero? (length routes)) -> (null? routes)
  5389. (if (eq? head 'zero?)
  5390. (lint-format "perhaps (assuming ~A is list) use null? instead of length: ~A" caller (cadr arg)
  5391. (lists->string form `(null? ,(cadr arg)))))))))))
  5392. ;; (zero? (logand...)) is nearly always preceded by not and handled elsewhere
  5393. (for-each (lambda (f)
  5394. (hash-special f sp-zero?))
  5395. '(zero? positive? negative?)))
  5396. ;; ---------------- / ----------------
  5397. (let ()
  5398. (define (sp-/ caller head form env)
  5399. (cond ((not (pair? (cdr form))))
  5400. ((and (null? (cddr form))
  5401. (number? (cadr form))
  5402. (zero? (cadr form))) ; (/ 0)
  5403. (lint-format "attempt to invert zero: ~A" caller (truncated-list->string form)))
  5404. ((and (pair? (cddr form)) ; (/ x y 2 0)
  5405. (memv 0 (cddr form)))
  5406. (lint-format "attempt to divide by 0: ~A" caller (truncated-list->string form)))
  5407. (else
  5408. (let ((len (assq 'length (cdr form))))
  5409. (if len (lint-format "~A will cause division by 0 if ~A is empty" caller len (cadr len)))))))
  5410. (hash-special '/ sp-/))
  5411. ;; ---------------- copy ----------------
  5412. (let ()
  5413. (define (sp-copy caller head form env)
  5414. (cond ((and (pair? (cdr form)) ; (copy (copy x)) could be (copy x)
  5415. (or (number? (cadr form))
  5416. (boolean? (cadr form))
  5417. (char? (cadr form))
  5418. (and (pair? (cadr form))
  5419. (memq (caadr form) '(copy string-copy))) ; or any maker?
  5420. (and (pair? (cddr form))
  5421. (equal? (cadr form) (caddr form)))))
  5422. (lint-format "~A could be ~A" caller (truncated-list->string form) (cadr form)))
  5423. ((and (pair? (cdr form)) ; (copy (owlet)) could be (owlet)
  5424. (equal? (cadr form) '(owlet)))
  5425. (lint-format "~A could be (owlet): owlet is copied internally" caller form))
  5426. ((= (length form) 5)
  5427. (check-start-and-end caller 'copy (cdddr form) form env))))
  5428. (hash-special 'copy sp-copy))
  5429. ;; ---------------- string-copy ----------------
  5430. (let ()
  5431. (define (sp-string-copy caller head form env)
  5432. (if (and (pair? (cdr form)) ; (string-copy (string-copy x)) could be (string-copy x)
  5433. (pair? (cadr form))
  5434. (memq (caadr form) '(copy string-copy string make-string string-upcase string-downcase
  5435. string-append list->string symbol->string number->string)))
  5436. (lint-format "~A could be ~A" caller (truncated-list->string form) (cadr form))))
  5437. (hash-special 'string-copy sp-string-copy))
  5438. ;; ---------------- string-down|upcase ----------------
  5439. (let ()
  5440. (define (sp-string-upcase caller head form env)
  5441. (if (and (pair? (cdr form))
  5442. (string? (cadr form))) ; (string-downcase "SPEAK") -> "speak"
  5443. (lint-format "perhaps ~A" caller (lists->string form
  5444. ((if (eq? head 'string-upcase) string-upcase string-downcase)
  5445. (cadr form))))))
  5446. (hash-special 'string-upcase sp-string-upcase)
  5447. (hash-special 'string-downcase sp-string-upcase))
  5448. ;; ---------------- string ----------------
  5449. (let ()
  5450. (define (sp-string caller head form env)
  5451. (if (every? (lambda (x)
  5452. (and (char? x)
  5453. (char<=? #\space x #\~))) ; #\0xx chars here look dumb
  5454. (cdr form))
  5455. (lint-format "~A could be ~S" caller (truncated-list->string form) (apply string (cdr form)))
  5456. (if (and (pair? (cdr form)) ; (string (string-ref x 0)) -> (substring x 0 1)
  5457. (pair? (cadr form)))
  5458. (if (and (eq? (caadr form) 'string-ref)
  5459. (null? (cddr form)))
  5460. (let ((arg (cdadr form)))
  5461. (if (integer? (cadr arg)) ; (string (string-ref x 0)) -> (substring x 0 1)
  5462. (lint-format "perhaps ~A" caller
  5463. (lists->string form
  5464. `(substring ,(car arg) ,(cadr arg) ,(+ 1 (cadr arg)))))))
  5465. (if (and (not (null? (cddr form)))
  5466. (memq (caadr form) '(char-upcase char-downcase))
  5467. (every? (lambda (p)
  5468. (eq? (caadr form) (car p)))
  5469. (cddr form)))
  5470. ;; (string (char-downcase (string-ref x 1)) (char-downcase (string-ref x 2))) ->
  5471. ;; (string-downcase (string (string-ref x 1) (string-ref x 2)))
  5472. (lint-format "perhaps ~A" caller
  5473. (lists->string form `(,(if (eq? (caadr form) 'char-upcase) 'string-upcase 'string-downcase)
  5474. (string ,@(map cadr (cdr form)))))))))))
  5475. ;; repeated args as in vector/list (sp-list below) got no hits
  5476. (hash-special 'string sp-string))
  5477. ;; ---------------- string? ----------------
  5478. (let ()
  5479. (define (sp-string? caller head form env)
  5480. (if (and (pair? (cdr form))
  5481. (pair? (cadr form))
  5482. (memq (caadr form) '(format number->string)))
  5483. (if (eq? (caadr form) 'format) ; (string? (number->string x)) -> #t
  5484. (lint-format "format returns either #f or a string, so ~A" caller (lists->string form (cadr form)))
  5485. (lint-format "number->string always returns a string, so ~A" caller (lists->string form #t)))
  5486. (check-boolean-affinity caller form env)))
  5487. (hash-special 'string? sp-string?))
  5488. ;; ---------------- number? ----------------
  5489. (let ()
  5490. (define (sp-number? caller head form env)
  5491. (if (and (pair? (cdr form))
  5492. (pair? (cadr form))
  5493. (eq? (caadr form) 'string->number)) ; (number? (string->number x)) -> (string->number x)
  5494. (lint-format "string->number returns either #f or a number, so ~A" caller (lists->string form (cadr form)))
  5495. (check-boolean-affinity caller form env)))
  5496. (hash-special 'number? sp-number?))
  5497. ;; ---------------- exact? inexact? infinite? nan? ----------------
  5498. (let ()
  5499. (define (sp-exact? caller head form env)
  5500. (if (and (pair? (cdr form))
  5501. (number? (cadr form)))
  5502. (check-boolean-affinity caller form env)))
  5503. (for-each (lambda (f)
  5504. (hash-special f sp-exact?))
  5505. '(exact? inexact? infinite? nan?)))
  5506. ;; ---------------- symbol? etc ----------------
  5507. (let ()
  5508. (define (sp-symbol? caller head form env)
  5509. (check-boolean-affinity caller form env))
  5510. (for-each (lambda (f)
  5511. (hash-special f sp-symbol?))
  5512. '(symbol? rational? real? complex? float? keyword? gensym? byte-vector? proper-list? sequence? constant?
  5513. char? boolean? float-vector? int-vector? vector? let? hash-table? input-port? c-object?
  5514. output-port? iterator? continuation? dilambda? procedure? macro? random-state? eof-object? c-pointer?)))
  5515. ;; ---------------- pair? list? ----------------
  5516. (let ()
  5517. (define (sp-pair? caller head form env)
  5518. (check-boolean-affinity caller form env)
  5519. (if (and (pair? (cdr form)) ; (pair? (member x y)) -> (member x y)
  5520. (pair? (cadr form))
  5521. (memq (caadr form) '(memq memv member assq assv assoc procedure-signature)))
  5522. (lint-format "~A returns either #f or a pair, so ~A" caller (caadr form)
  5523. (lists->string form (cadr form)))))
  5524. (for-each (lambda (f)
  5525. (hash-special f sp-pair?))
  5526. '(pair? list?)))
  5527. ;; ---------------- integer? ----------------
  5528. (let ()
  5529. (define (sp-integer? caller head form env)
  5530. (check-boolean-affinity caller form env)
  5531. (if (and (pair? (cdr form)) ; (integer? (char-position x y)) -> (char-position x y)
  5532. (pair? (cadr form))
  5533. (memq (caadr form) '(char-position string-position)))
  5534. (lint-format "~A returns either #f or an integer, so ~A" caller (caadr form)
  5535. (lists->string form (cadr form)))))
  5536. (hash-special 'integer? sp-integer?))
  5537. ;; ---------------- null? ----------------
  5538. (let ()
  5539. (define (sp-null? caller head form env)
  5540. (check-boolean-affinity caller form env)
  5541. (if (and (pair? (cdr form)) ; (null? (string->list x)) -> (zero? (length x))
  5542. (pair? (cadr form))
  5543. (memq (caadr form) '(vector->list string->list let->list)))
  5544. (lint-format "perhaps ~A" caller
  5545. (lists->string form `(zero? (length ,(cadadr form)))))))
  5546. (hash-special 'null? sp-null?))
  5547. ;; ---------------- odd? even? ----------------
  5548. (let ()
  5549. (define (sp-odd? caller head form env)
  5550. (if (and (pair? (cdr form)) ; (odd? (- x 1)) -> (even? x)
  5551. (pair? (cadr form))
  5552. (memq (caadr form) '(+ -))
  5553. (= (length (cadr form)) 3))
  5554. (let* ((arg1 (cadadr form))
  5555. (arg2 (caddr (cadr form)))
  5556. (int-arg (or (and (integer? arg1) arg1)
  5557. (and (integer? arg2) arg2))))
  5558. (if int-arg
  5559. (lint-format "perhaps ~A" caller
  5560. (lists->string form
  5561. (if (and (integer? arg1)
  5562. (integer? arg2))
  5563. (eval form)
  5564. `(,(if (eq? (eq? head 'even?) (even? int-arg)) 'even? 'odd?)
  5565. ,(if (integer? arg1) arg2 arg1)))))))))
  5566. (hash-special 'odd? sp-odd?)
  5567. (hash-special 'even? sp-odd?))
  5568. ;; ---------------- string-ref ----------------
  5569. (let ()
  5570. (define (sp-string-ref caller head form env)
  5571. (when (= (length form) 3)
  5572. (if (equal? (cadr form) "")
  5573. (lint-format "~A is an error" caller form)
  5574. (when (every? code-constant? (cdr form)) ; (string-ref "abc" 0) -> #\a
  5575. (catch #t
  5576. (lambda ()
  5577. (let ((val (eval form)))
  5578. (lint-format "perhaps ~A" caller (lists->string form val))))
  5579. (lambda args
  5580. (lint-format "~A: ~A" caller
  5581. (object->string form)
  5582. (apply format #f (cadr args)))))))
  5583. (when (pair? (cadr form))
  5584. (let ((target (cadr form)))
  5585. (case (car target)
  5586. ((substring) ; (string-ref (substring x 1) 2) -> (string-ref x (+ 2 1))
  5587. (if (= (length target) 3)
  5588. (lint-format "perhaps ~A" caller (lists->string form `(string-ref ,(cadr target) (+ ,(caddr form) ,(caddr target)))))))
  5589. ((symbol->string) ; (string-ref (symbol->string 'abs) 1) -> #\b
  5590. (if (and (integer? (caddr form))
  5591. (pair? (cadr target))
  5592. (eq? (caadr target) 'quote)
  5593. (symbol? (cadadr target)))
  5594. (lint-format "perhaps ~A" caller (lists->string form (string-ref (symbol->string (cadadr target)) (caddr form))))))
  5595. ((make-string) ; (string-ref (make-string 3 #\a) 1) -> #\a
  5596. (if (and (integer? (cadr target))
  5597. (integer? (caddr form))
  5598. (> (cadr target) (caddr form)))
  5599. (lint-format "perhaps ~A" caller (lists->string form (if (= (length target) 3) (caddr target) #\space))))))))))
  5600. (hash-special 'string-ref sp-string-ref))
  5601. ;; ---------------- vector-ref etc ----------------
  5602. (let ()
  5603. (define (sp-vector-ref caller head form env)
  5604. (unless (= line-number last-checker-line-number)
  5605. (when (= (length form) 3)
  5606. (let ((seq (cadr form)))
  5607. (when (code-constant? (cadr form))
  5608. (if (eqv? (length (cadr form)) 0)
  5609. (lint-format "~A is an error" caller form)
  5610. (when (every? code-constant? (cddr form)) ; (vector-ref #(1 2) 0) -> 1
  5611. (catch #t
  5612. (lambda ()
  5613. (let ((val (eval form)))
  5614. (lint-format "perhaps ~A -> ~A~A" caller
  5615. (truncated-list->string form)
  5616. (if (or (pair? val)
  5617. (symbol? val))
  5618. "'" "")
  5619. (object->string val))))
  5620. (lambda args
  5621. (lint-format "~A: ~A" caller
  5622. (object->string form)
  5623. (apply format #f (cadr args))))))))
  5624. (when (pair? seq)
  5625. (if (and (memq (car seq) '(vector-ref int-vector-ref float-vector-ref list-ref hash-table-ref let-ref))
  5626. (= (length seq) 3)) ; (vector-ref (vector-ref x i) j) -> (x i j)
  5627. (let ((seq1 (cadr seq))) ; x
  5628. (lint-format "perhaps ~A" caller
  5629. (lists->string form
  5630. (if (and (pair? seq1) ; (vector-ref (vector-ref (vector-ref x i) j) k) -> (x i j k)
  5631. (memq (car seq1) '(vector-ref int-vector-ref float-vector-ref list-ref hash-table-ref let-ref))
  5632. (= (length seq1) 3))
  5633. `(,(cadr seq1) ,(caddr seq1) ,(caddr seq) ,(caddr form))
  5634. `(,seq1 ,(caddr seq) ,(caddr form))))))
  5635. (if (memq (car seq) '(make-vector make-list vector list
  5636. make-float-vector make-int-vector float-vector int-vector
  5637. make-hash-table hash-table hash-table*
  5638. inlet))
  5639. (lint-format "this doesn't make much sense: ~A" caller form)))
  5640. (when (eq? head 'list-ref)
  5641. (if (eq? (car seq) 'quote)
  5642. (if (proper-list? (cadr seq)) ; (list-ref '(#t #f) (random 2)) -> (vector-ref #(#t #f) (random 2))
  5643. (lint-format "perhaps use a vector: ~A" caller
  5644. (lists->string form `(vector-ref ,(apply vector (cadr seq)) ,(caddr form)))))
  5645. (let ((index (caddr form))) ; (list-ref (cdddr f) 2) -> (list-ref f 5)
  5646. (if (and (memq (car seq) '(cdr cddr cdddr))
  5647. (or (integer? index)
  5648. (and (pair? index)
  5649. (eq? (car index) '-)
  5650. (integer? (caddr index)))))
  5651. (let ((offset (cdr (assq (car seq) '((cdr . 1) (cddr . 2) (cdddr . 3))))))
  5652. (lint-format "perhaps ~A" caller
  5653. (lists->string form
  5654. `(list-ref ,(cadr seq)
  5655. ,(if (integer? index)
  5656. (+ index offset)
  5657. (let ((noff (- (caddr index) offset)))
  5658. (if (zero? noff)
  5659. (cadr index)
  5660. `(- ,(cadr index) ,noff)))))))))))))))
  5661. (set! last-checker-line-number line-number)))
  5662. (for-each (lambda (f)
  5663. (hash-special f sp-vector-ref))
  5664. '(vector-ref list-ref hash-table-ref let-ref int-vector-ref float-vector-ref)))
  5665. ;; ---------------- vector-set! etc ----------------
  5666. (let ()
  5667. (define (sp-vector-set! caller head form env)
  5668. (when (= (length form) 4)
  5669. (let ((target (cadr form))
  5670. (index (caddr form))
  5671. (val (cadddr form)))
  5672. (cond ((and (pair? val) ; (vector-set! x 0 (vector-ref x 0))
  5673. (= (length val) 3)
  5674. (eq? target (cadr val))
  5675. (equal? index (caddr val))
  5676. (memq (car val) '(vector-ref list-ref hash-table-ref string-ref let-ref float-vector-ref int-vector-ref)))
  5677. (lint-format "redundant ~A: ~A" caller head (truncated-list->string form)))
  5678. ((code-constant? target) ; (vector-set! #(0 1 2) 1 3)??
  5679. (lint-format "~A is a constant that is discarded; perhaps ~A" caller target (lists->string form val)))
  5680. ((not (pair? target)))
  5681. ((and (not (eq? head 'string-set!)) ; (vector-set! (vector-ref x 0) 1 2) -- vector within vector
  5682. (memq (car target) '(vector-ref list-ref hash-table-ref let-ref float-vector-ref int-vector-ref)))
  5683. (lint-format "perhaps ~A" caller (lists->string form `(set! (,@(cdr target) ,index) ,val))))
  5684. ((memq (car target) '(make-vector vector make-string string make-list list append cons
  5685. vector-append inlet sublet copy vector-copy string-copy list-copy)) ;list-copy is from r7rs
  5686. (lint-format "~A is simply discarded; perhaps ~A" caller
  5687. (truncated-list->string target) ; (vector-set! (make-vector 3) 1 1) -- does this ever happen?
  5688. (lists->string form val)))
  5689. ((and (eq? head 'list-set!)
  5690. (memq (car target) '(cdr cddr cdddr cddddr))
  5691. (integer? (caddr form))) ; (list-set! (cdr x) 0 y) -> (list-set! x 1 y)
  5692. (lint-format "perhaps ~A" caller
  5693. (lists->string form
  5694. `(list-set! ,(cadr target) ,(+ (caddr form) (cdr-count (car target))) ,(cadddr form)))))))))
  5695. (for-each (lambda (f)
  5696. (hash-special f sp-vector-set!))
  5697. '(vector-set! list-set! hash-table-set! float-vector-set! int-vector-set! string-set! let-set!)))
  5698. ;; ---------------- object->string ----------------
  5699. (let ()
  5700. (define (sp-object->string caller head form env)
  5701. (when (pair? (cdr form))
  5702. (if (and (pair? (cadr form)) ; (object->string (object->string x)) could be (object->string x)
  5703. (eq? (caadr form) 'object->string))
  5704. (lint-format "~A could be ~A" caller (truncated-list->string form) (cadr form))
  5705. (if (pair? (cddr form))
  5706. (let ((arg2 (caddr form)))
  5707. (if (and (code-constant? arg2) ; (object->string x :else)
  5708. (not (memq arg2 '(#f #t :readable)))) ; #f and #t are display|write choice, :readable = ~W
  5709. (lint-format "bad second argument: ~A" caller arg2)))))))
  5710. (hash-special 'object->string sp-object->string))
  5711. (define (all-caps-warning arg)
  5712. (and (string? arg)
  5713. (or (string-position "ERROR" arg)
  5714. (string-position "WARNING" arg))))
  5715. ;; ---------------- display ----------------
  5716. (let ()
  5717. (define (sp-display caller head form env)
  5718. (when (pair? (cdr form))
  5719. (let ((arg (cadr form))
  5720. (port (if (pair? (cddr form))
  5721. (caddr form)
  5722. ())))
  5723. (cond ((all-caps-warning arg)
  5724. (lint-format "There's no need to shout: ~A" caller (truncated-list->string form)))
  5725. ((not (and (pair? arg)
  5726. (pair? (cdr arg)))))
  5727. ((and (eq? (car arg) 'format) ; (display (format #f str x)) -> (format () str x)
  5728. (not (cadr arg)))
  5729. (lint-format "perhaps ~A" caller (lists->string form `(format ,port ,@(cddr arg)))))
  5730. ((and (eq? (car arg) 'apply) ; (display (apply format #f str x) p) -> (apply format p str x)
  5731. (eq? (cadr arg) 'format)
  5732. (pair? (cddr arg))
  5733. (not (caddr arg)))
  5734. (lint-format "perhaps ~A" caller (lists->string form `(apply format ,port ,@(cdddr arg)))))
  5735. ((and (pair? port)
  5736. (eq? (car port) 'current-output-port))
  5737. (lint-format "(current-output-port) is the default port for display: ~A" caller form))))))
  5738. (hash-special 'display sp-display))
  5739. ;; ---------------- flush-output-port, newline, close-output-port ----------------
  5740. (let ()
  5741. (define (sp-flush-output-port caller head form env)
  5742. (if (and (pair? (cdr form))
  5743. (pair? (cadr form))
  5744. (eq? (caadr form) 'current-output-port))
  5745. (lint-format "(current-output-port) is the default port for ~A: ~A" caller head form)))
  5746. (hash-special 'flush-output-port sp-flush-output-port)
  5747. (hash-special 'close-output-port sp-flush-output-port)
  5748. (hash-special 'newline sp-flush-output-port))
  5749. ;; ---------------- write-char, write-byte, write ----------------
  5750. (let ()
  5751. (define (sp-write-char caller head form env)
  5752. (when (pair? (cdr form))
  5753. (if (and (pair? (cddr form))
  5754. (pair? (caddr form))
  5755. (eq? (caaddr form) 'current-output-port))
  5756. (lint-format "(current-output-port) is the default port for ~A: ~A" caller head form))
  5757. (if (and (eq? head 'write-byte)
  5758. (integer? (cadr form))
  5759. (not (<= 0 (cadr form) 255)))
  5760. (lint-format "write-byte argument must be (<= 0 byte 255): ~A" caller form)
  5761. (if (and (eq? head 'write-char)
  5762. (eqv? (cadr form) #\newline))
  5763. (lint-format "perhaps ~A" caller (lists->string form `(newline ,@(cddr form))))))))
  5764. (hash-special 'write-char sp-write-char)
  5765. (hash-special 'write-byte sp-write-char)
  5766. (hash-special 'write sp-write-char))
  5767. ;; ---------------- read, port-filename, port-line-number, read-char, read-byte ----------------
  5768. (let ()
  5769. (define (sp-read caller head form env)
  5770. (when (and (pair? (cdr form))
  5771. (null? (cddr form)))
  5772. (if (and (pair? (cadr form))
  5773. (eq? (caadr form) 'current-input-port))
  5774. (lint-format "(current-input-port) is the default port for ~A: ~A" caller head form)
  5775. (if (and (eq? head 'port-filename)
  5776. (memq (cadr form) '(*stdin* *stdout* *stderr*)))
  5777. (lint-format "~A: ~S" caller form
  5778. (case (cadr form) ((*stdin*) "*stdin*") ((*stdout*) "*stdout*") ((*stderr*) "*stderr*")))))))
  5779. (for-each (lambda (c)
  5780. (hash-special c sp-read))
  5781. '(read port-filename port-line-number read-char read-byte peek-char close-input-port)))
  5782. ;; ---------------- char-alphabetic? char-lower-case? char-numeric? char-upper-case? char-whitespace? etc ----------------
  5783. (let ()
  5784. (define (sp-char-numeric caller head form env)
  5785. (if (and (not (var-member (car form) env))
  5786. (pair? (cdr form))
  5787. (null? (cddr form))
  5788. (char? (cadr form)))
  5789. (lint-format "perhaps ~A" caller (lists->string form (eval form)))))
  5790. (for-each (lambda (c)
  5791. (hash-special c sp-char-numeric))
  5792. '(char-alphabetic? char-lower-case? char-numeric? char-upper-case? char-whitespace? char-upcase char-downcase)))
  5793. ;; ---------------- make-vector etc ----------------
  5794. (let ()
  5795. (define (sp-make-vector caller head form env)
  5796. ;; type of initial value (for make-float|int-vector) is checked elsewhere
  5797. (if (and (= (length form) 4)
  5798. (eq? head 'make-vector)) ; (make-vector 3 0 #t)
  5799. (lint-format "make-vector no longer has a fourth argument: ~A" caller form))
  5800. (if (>= (length form) 3)
  5801. (case (caddr form)
  5802. ((#<unspecified>)
  5803. (if (eq? head 'make-vector) ; (make-vector 3 #<unspecified>)
  5804. (lint-format "#<unspecified> is the default initial value in ~A" caller form)))
  5805. ((0)
  5806. (if (not (eq? head 'make-vector))
  5807. (lint-format "0 is the default initial value in ~A" caller form)))
  5808. ((0.0)
  5809. (if (eq? head 'make-float-vector)
  5810. (lint-format "0.0 is the default initial value in ~A" caller form)))))
  5811. (when (and (pair? (cdr form))
  5812. (integer? (cadr form))
  5813. (zero? (cadr form)))
  5814. (if (pair? (cddr form)) ; (make-vector 0 0.0)
  5815. (lint-format "initial value is pointless here: ~A" caller form))
  5816. (lint-format "perhaps ~A" caller (lists->string form #()))))
  5817. (for-each (lambda (f)
  5818. (hash-special f sp-make-vector))
  5819. '(make-vector make-int-vector make-float-vector)))
  5820. ;; ---------------- make-string make-byte-vector ----------------
  5821. (let ()
  5822. (define (sp-make-string caller head form env)
  5823. (when (and (pair? (cdr form))
  5824. (integer? (cadr form))
  5825. (zero? (cadr form)))
  5826. (if (pair? (cddr form)) ; (make-byte-vector 0 0)
  5827. (lint-format "initial value is pointless here: ~A" caller form))
  5828. (lint-format "perhaps ~A" caller (lists->string form "")))) ; #u8() but (equal? #u8() "") -> #t so lint combines these clauses!
  5829. (for-each (lambda (f)
  5830. (hash-special f sp-make-string))
  5831. '(make-string make-byte-vector)))
  5832. ;; ---------------- make-list ----------------
  5833. (let ()
  5834. (define (sp-make-list caller head form env)
  5835. (when (and (pair? (cdr form))
  5836. (integer? (cadr form))
  5837. (zero? (cadr form)))
  5838. (if (pair? (cddr form)) ; (make-list 0 #f)
  5839. (lint-format "initial value is pointless here: ~A" caller form))
  5840. (lint-format "perhaps ~A" caller (lists->string form ()))))
  5841. (hash-special 'make-list sp-make-list))
  5842. ;; ---------------- reverse string->list etc ----------------
  5843. (let ()
  5844. (define (sp-reverse caller head form env)
  5845. ;; not string->number -- no point in copying a number and it's caught below
  5846. (when (pair? (cdr form))
  5847. (if (code-constant? (cadr form))
  5848. (let ((seq (checked-eval form)))
  5849. (if (not (eq? seq :checked-eval-error)) ; (symbol->string 'abs) -> "abs"
  5850. (lint-format "perhaps ~A -> ~A~A" caller
  5851. (truncated-list->string form)
  5852. (if (pair? seq) "'" "")
  5853. (if (symbol? seq)
  5854. (object->string seq :readable)
  5855. (object->string seq))))))
  5856. (when (and (pair? (cadr form))
  5857. (pair? (cdadr form)))
  5858. (let ((inverses '((reverse . reverse)
  5859. (reverse! . reverse!)
  5860. ;; reverse and reverse! are not completely interchangable:
  5861. ;; (reverse (cons 1 2)): (2 . 1)
  5862. ;; (reverse! (cons 1 2)): error: reverse! argument, (1 . 2), is a pair but should be a proper list
  5863. (list->vector . vector->list)
  5864. (vector->list . list->vector)
  5865. (symbol->string . string->symbol)
  5866. (string->symbol . symbol->string)
  5867. (list->string . string->list)
  5868. (string->list . list->string)
  5869. (number->string . string->number))))
  5870. (let ((inv-op (assq head inverses))
  5871. (arg (cadr form))
  5872. (arg-args (cdadr form))
  5873. (arg-of-arg (cadadr form))
  5874. (func-of-arg (caadr form)))
  5875. (if (pair? inv-op) (set! inv-op (cdr inv-op)))
  5876. (cond ((eq? func-of-arg inv-op) ; (vector->list (list->vector x)) -> x
  5877. (if (eq? head 'string->symbol)
  5878. (lint-format "perhaps ~A" caller (lists->string form arg-of-arg))
  5879. (lint-format "~A could be (copy ~S)" caller form arg-of-arg)))
  5880. ((and (eq? head 'list->string) ; (list->string (vector->list x)) -> (copy x (make-string (length x)))
  5881. (eq? func-of-arg 'vector->list))
  5882. (lint-format "perhaps ~A" caller (lists->string form `(copy ,arg-of-arg (make-string (length ,arg-of-arg))))))
  5883. ((and (eq? head 'list->string) ; (list->string (make-list x y)) -> (make-string x y)
  5884. (eq? func-of-arg 'make-list))
  5885. (lint-format "perhaps ~A" caller (lists->string form `(make-string ,@arg-args))))
  5886. ((and (eq? head 'string->list) ; (string->list (string x y)) -> (list x y)
  5887. (eq? func-of-arg 'string))
  5888. (lint-format "perhaps ~A" caller (lists->string form `(list ,@arg-args))))
  5889. ((and (eq? head 'list->vector) ; (list->vector (make-list ...)) -> (make-vector ...)
  5890. (eq? func-of-arg 'make-list))
  5891. (lint-format "perhaps ~A" caller (lists->string form `(make-vector ,@arg-args))))
  5892. ((and (eq? head 'list->vector) ; (list->vector (string->list x)) -> (copy x (make-vector (length x)))
  5893. (eq? func-of-arg 'string->list))
  5894. (lint-format "perhaps ~A" caller (lists->string form `(copy ,arg-of-arg (make-vector (length ,arg-of-arg))))))
  5895. ((and (eq? head 'list->vector) ; (list->vector (append (vector->list v1) ...)) -> (append v1 ...)
  5896. (eq? func-of-arg 'append)
  5897. (every? (lambda (a)
  5898. (and (pair? a)
  5899. (eq? (car a) 'vector->list)))
  5900. (cdadr form)))
  5901. (lint-format "perhaps ~A" caller
  5902. (lists->string form `(append ,@(map cadr (cdadr form))))))
  5903. ((and (eq? head 'vector->list) ; (vector->list (make-vector ...)) -> (make-list ...)
  5904. (eq? func-of-arg 'make-vector))
  5905. (lint-format "perhaps ~A" caller (lists->string form `(make-list ,@arg-args))))
  5906. ((and (eq? head 'vector->list) ; (vector->list (vector ...)) -> (list ...)
  5907. (eq? func-of-arg 'vector))
  5908. (lint-format "perhaps ~A" caller (lists->string form `(list ,@arg-args))))
  5909. ((and (eq? head 'vector->list) ; (vector->list (vector-copy ...)) -> (vector->list ...)
  5910. (eq? func-of-arg 'vector-copy))
  5911. (lint-format "perhaps ~A" caller (lists->string form `(vector->list ,@arg-args))))
  5912. ((and (memq func-of-arg '(reverse reverse! copy))
  5913. (pair? arg-of-arg) ; (list->string (reverse (string->list x))) -> (reverse x)
  5914. (eq? (car arg-of-arg) inv-op))
  5915. (lint-format "perhaps ~A" caller (lists->string form `(,(if (eq? func-of-arg 'reverse!) 'reverse func-of-arg) ,(cadr arg-of-arg)))))
  5916. ((and (memq head '(reverse reverse!)) ; (reverse (string->list x)) -> (string->list (reverse x)) -- often redundant
  5917. (memq func-of-arg '(string->list vector->list sort!)))
  5918. (cond ((not (eq? func-of-arg 'sort!))
  5919. (if (null? (cdr arg-args))
  5920. (lint-format "perhaps less consing: ~A" caller
  5921. (lists->string form `(,func-of-arg (reverse ,arg-of-arg))))))
  5922. ((and (pair? arg-args) ; (reverse (sort! x <)) -> (sort x >)
  5923. (pair? (cdr arg-args))
  5924. (hash-table-ref reversibles (cadr arg-args)))
  5925. => (lambda (op)
  5926. (lint-format "possibly ~A" caller (lists->string form `(sort! ,arg-of-arg ,op)))))))
  5927. ((and (pair? arg-of-arg)
  5928. (memq func-of-arg '(cdr cddr cdddr cddddr list-tail))
  5929. (case head
  5930. ((list->string) (eq? (car arg-of-arg) 'string->list))
  5931. ((list->vector) (eq? (car arg-of-arg) 'vector->list))
  5932. (else #f)))
  5933. (let ((len-diff (if (eq? func-of-arg 'list-tail)
  5934. (cadr arg-args)
  5935. (cdr-count func-of-arg))))
  5936. (lint-format "perhaps ~A" caller ; (list->string (cdr (string->list x))) -> (substring x 1)
  5937. (lists->string form (if (eq? head 'list->string)
  5938. `(substring ,(cadr arg-of-arg) ,len-diff)
  5939. `(copy ,(cadr arg-of-arg) (make-vector (- (length ,(cadr arg-of-arg)) ,len-diff))))))))
  5940. ((and (memq head '(list->vector list->string))
  5941. (eq? func-of-arg 'sort!) ; (list->vector (sort! (vector->list x) y)) -> (sort! x y)
  5942. (pair? arg-of-arg)
  5943. (eq? (car arg-of-arg) (if (eq? head 'list->vector) 'vector->list 'string->list)))
  5944. (lint-format "perhaps ~A" caller (lists->string form `(sort! ,(cadr arg-of-arg) ,(cadr arg-args)))))
  5945. ((and (memq head '(list->vector list->string))
  5946. (or (memq func-of-arg '(list cons))
  5947. (quoted-undotted-pair? arg)))
  5948. (let ((maker (if (eq? head 'list->vector) 'vector 'string)))
  5949. (case func-of-arg
  5950. ((list)
  5951. (if (var-member maker env) ; (list->string (list x y z)) -> (string x y z)
  5952. (lint-format "~A could be simplified, but you've shadowed '~A" caller (truncated-list->string form) maker)
  5953. (lint-format "perhaps ~A" caller (lists->string form `(,maker ,@arg-args)))))
  5954. ((cons)
  5955. (if (any-null? (cadr arg-args))
  5956. (if (var-member maker env) ; (list->string (cons x ())) -> (string x)
  5957. (lint-format "~A could be simplified, but you've shadowed '~A" caller (truncated-list->string form) maker)
  5958. (lint-format "perhaps ~A" caller (lists->string form `(,maker ,arg-of-arg)))))))))
  5959. ((and (memq head '(list->string list->vector)) ; (list->string (reverse x)) -> (reverse (apply string x))
  5960. (memq func-of-arg '(reverse reverse!)))
  5961. (lint-format "perhaps ~A" caller (lists->string form `(reverse (,head ,arg-of-arg)))))
  5962. ((and (eq? head 'string->symbol) ; (string->symbol (string-append...)) -> (symbol ...)
  5963. (or (memq func-of-arg '(string-append append))
  5964. (and (eq? func-of-arg 'apply)
  5965. (memq arg-of-arg '(string-append append)))))
  5966. (lint-format "perhaps ~A" caller
  5967. (lists->string form
  5968. (if (eq? func-of-arg 'apply)
  5969. `(apply symbol ,@(cdr arg-args))
  5970. `(symbol ,@arg-args)))))
  5971. ((and (eq? head 'string->symbol) ; (string->symbol (if (not (null? x)) x "abc")) -> (if (not (null? x)) (string->symbol x) 'abc)
  5972. (eq? func-of-arg 'if)
  5973. (or (string? (cadr arg-args))
  5974. (string? (caddr arg-args)))
  5975. (not (or (equal? (cadr arg-args) "") ; this is actually an error -- should we complain?
  5976. (equal? (caddr arg-args) ""))))
  5977. (lint-format "perhaps ~A" caller
  5978. (lists->string form
  5979. (if (string? (cadr arg-args))
  5980. (if (string? (caddr arg-args))
  5981. `(if ,arg-of-arg ',(string->symbol (cadr arg-args)) ',(string->symbol (caddr arg-args)))
  5982. `(if ,arg-of-arg ',(string->symbol (cadr arg-args)) (string->symbol ,(caddr arg-args))))
  5983. `(if ,arg-of-arg (string->symbol ,(cadr arg-args)) ',(string->symbol (caddr arg-args)))))))
  5984. ((case head ; (reverse (reverse! x)) could be (copy x)
  5985. ((reverse) (eq? func-of-arg 'reverse!))
  5986. ((reverse!) (eq? func-of-arg 'reverse))
  5987. (else #f))
  5988. (lint-format "~A could be (copy ~S)" caller form arg-of-arg))
  5989. ((and (pair? arg-of-arg) ; (op (reverse (inv-op x))) -> (reverse x)
  5990. (eq? func-of-arg 'reverse)
  5991. (eq? inv-op (car arg-of-arg)))
  5992. (lint-format "perhaps ~A" caller (lists->string form `(reverse ,(cadr arg-of-arg)))))))))
  5993. (when (pair? (cddr form)) ; (string->list x y y) is ()
  5994. (when (and (memq head '(vector->list string->list))
  5995. (pair? (cdddr form)))
  5996. (check-start-and-end caller head (cddr form) form env))
  5997. (when (and (eq? head 'number->string) ; (number->string saturation 10)
  5998. (eqv? (caddr form) 10))
  5999. (lint-format "10 is the default radix for number->string: ~A" caller (truncated-list->string form))))
  6000. (when (memq head '(reverse reverse!))
  6001. (if (and (eq? head 'reverse!)
  6002. (symbol? (cadr form)))
  6003. (let ((v (var-member (cadr form) env)))
  6004. (if (and (var? v)
  6005. (eq? (var-definer v) 'parameter))
  6006. (lint-format "if ~A (a function argument) is a pair, ~A is ill-advised" caller
  6007. (cadr form)
  6008. (truncated-list->string form))))
  6009. (when (pair? (cadr form))
  6010. (let ((arg (cadr form))
  6011. (arg-op (caadr form))
  6012. (arg-args (cdadr form))
  6013. (arg-arg (and (pair? (cdadr form)) (cadadr form))))
  6014. (when (and (pair? arg-args)
  6015. (pair? arg-arg))
  6016. (if (and (memq arg-op '(cdr list-tail)) ; (reverse (cdr (reverse lst))) = all but last of lst -> copy to len-1
  6017. (memq (car arg-arg) '(reverse reverse!))
  6018. (symbol? (cadr arg-arg)))
  6019. (lint-format "perhaps ~A" caller
  6020. (lists->string form `(copy ,(cadr arg-arg)
  6021. (make-list (- (length ,(cadr arg-arg)) ,(if (eq? arg-op 'cdr) 1 (cadr arg-args))))))))
  6022. (if (and (eq? arg-op 'append) ; (reverse (append (reverse b) res)) = (append (reverse res) b)
  6023. (eq? (car arg-arg) 'reverse)
  6024. (pair? (cdr arg-args))
  6025. (null? (cddr arg-args)))
  6026. (lint-format "perhaps ~A" caller (lists->string form `(append (reverse ,(cadr arg-args)) ,(cadr arg-arg))))))
  6027. (when (and (= (length arg) 3)
  6028. (pair? (cadr arg-args)))
  6029. (cond ((and (eq? arg-op 'map) ; (reverse (map abs (sort! x <))) -> (map abs (sort! x >))
  6030. (eq? (caadr arg-args) 'sort!)
  6031. (hash-table-ref reversibles (caddr (cadr arg-args))))
  6032. => (lambda (op)
  6033. (lint-format "possibly ~A" caller (lists->string form `(,arg-op ,arg-arg (sort! ,(cadadr arg-args) ,op)))))))
  6034. ;; (reverse (apply vector (sort! x <))) doesn't happen (nor does this map case, but it's too pretty to leave out)
  6035. (if (and (eq? arg-op 'cons) ; (reverse (cons x (reverse lst))) -- adds x to end -- (append lst (list x))
  6036. (memq (caadr arg-args) '(reverse reverse!)))
  6037. (lint-format "perhaps ~A" caller (lists->string form `(append ,(cadadr arg-args) (list ,arg-arg))))))))))))
  6038. (for-each (lambda (f)
  6039. (hash-special f sp-reverse))
  6040. '(reverse reverse! list->vector vector->list list->string string->list symbol->string string->symbol number->string)))
  6041. ;; ---------------- char->integer string->number etc ----------------
  6042. (let ()
  6043. (define (sp-char->integer caller head form env)
  6044. (when (pair? (cdr form))
  6045. (let ((inverses '((char->integer . integer->char)
  6046. (integer->char . char->integer)
  6047. (symbol->keyword . keyword->symbol)
  6048. (keyword->symbol . symbol->keyword)
  6049. (string->number . number->string)))
  6050. (arg (cadr form)))
  6051. (if (and (pair? arg)
  6052. (pair? (cdr arg)) ; (string->number (number->string x)) could be x
  6053. (eq? (car arg) (cond ((assq head inverses) => cdr))))
  6054. (lint-format "~A could be ~A" caller (truncated-list->string form) (cadr arg))
  6055. (case head
  6056. ((integer->char)
  6057. (if (let walk ((tree (cdr form)))
  6058. (if (pair? tree)
  6059. (and (walk (car tree))
  6060. (walk (cdr tree)))
  6061. (or (code-constant? tree)
  6062. (not (side-effect? tree env)))))
  6063. (let ((chr (checked-eval form))) ; (integer->char (+ (char->integer #\space) 215)) -> #\xf7
  6064. (if (char? chr)
  6065. (lint-format "perhaps ~A" caller (lists->string form chr))))))
  6066. ((string->number)
  6067. (if (and (pair? (cddr form))
  6068. (integer? (caddr form)) ; type error is checked elsewhere
  6069. (not (<= 2 (caddr form) 16))) ; (string->number "123" 21)
  6070. (lint-format "string->number radix should be between 2 and 16: ~A" caller form)
  6071. (if (and (pair? arg)
  6072. (eq? (car arg) 'string)
  6073. (pair? (cdr arg))
  6074. (null? (cddr form))
  6075. (null? (cddr arg))) ; (string->number (string num-char)) -> (- (char->integer num-char) (char->integer #\0))
  6076. (lint-format "perhaps ~A" caller
  6077. (lists->string form `(- (char->integer ,(cadr arg)) (char->integer #\0)))))))
  6078. ((symbol->keyword)
  6079. (if (and (pair? arg) ; (symbol->keyword (string->symbol x)) -> (make-keyword x)
  6080. (eq? (car arg) 'string->symbol))
  6081. (lint-format "perhaps ~A" caller (lists->string form `(make-keyword ,(cadr arg))))
  6082. (if (quoted-symbol? arg)
  6083. (lint-format "perhaps ~A" caller (lists->string form (symbol->keyword (cadr arg)))))))
  6084. ((keyword->symbol)
  6085. (if (keyword? arg)
  6086. (lint-format "perhaps ~A -> '~A" caller (object->string form) (object->string (keyword->symbol arg))))))))))
  6087. (for-each (lambda (f)
  6088. (hash-special f sp-char->integer))
  6089. '(char->integer integer->char symbol->keyword keyword->symbol string->number)))
  6090. ;; ---------------- string-append ----------------
  6091. (let ()
  6092. (define (sp-string-append caller head form env)
  6093. (unless (= line-number last-checker-line-number)
  6094. (let ((args (remove-all "" (splice-if (lambda (x) (eq? x 'string-append)) (cdr form))))
  6095. (combined #f))
  6096. (when (or (any? string? args)
  6097. (member 'string args (lambda (a b) (and (pair? b) (eq? (car b) a)))))
  6098. (do ((nargs ()) ; look for (string...) (string...) in the arg list and combine
  6099. (p args (cdr p)))
  6100. ((null? p)
  6101. (set! args (reverse nargs)))
  6102. (cond ((not (pair? (cdr p)))
  6103. (set! nargs (cons (car p) nargs)))
  6104. ((and (pair? (car p))
  6105. (eq? (caar p) 'string)
  6106. (pair? (cadr p))
  6107. (eq? (caadr p) 'string))
  6108. (set! nargs (cons `(string ,@(cdar p) ,@(cdadr p)) nargs))
  6109. (set! combined #t)
  6110. (set! p (cdr p)))
  6111. ((and (string? (car p))
  6112. (string? (cadr p)))
  6113. (set! nargs (cons (string-append (car p) (cadr p)) nargs))
  6114. (set! combined #t)
  6115. (set! p (cdr p)))
  6116. (else (set! nargs (cons (car p) nargs))))))
  6117. (cond ((null? args) ; (string-append) -> ""
  6118. (lint-format "perhaps ~A" caller (lists->string form "")))
  6119. ((null? (cdr args)) ; (string-append a) -> a
  6120. (if (not (tree-memq 'values (cdr form)))
  6121. (lint-format "perhaps ~A~A" caller (lists->string form (car args))
  6122. (if combined "" ", or use copy")))) ; (string-append x "") appears to be a common substitute for string-copy
  6123. ((every? string? args) ; (string-append "a" "b") -> "ab"
  6124. (lint-format "perhaps ~A" caller (lists->string form (apply string-append args))))
  6125. ((every? (lambda (a) ; (string-append "a" (string #\b)) -> "ab"
  6126. (or (string? a)
  6127. (and (pair? a)
  6128. (eq? (car a) 'string)
  6129. (char? (cadr a)))))
  6130. args)
  6131. (catch #t
  6132. (lambda () ; (string-append (string #\C) "ZLl*()def") -> "CZLl*()def"
  6133. (let ((val (if (not (any? pair? args))
  6134. (apply string-append args)
  6135. (eval (cons 'string-append args)))))
  6136. (lint-format "perhaps ~A -> ~S" caller (truncated-list->string form) val)))
  6137. (lambda args #f)))
  6138. ((every? (lambda (c) ; (string-append (make-string 3 #\a) (make-string 2 #\b)) -> (format #f "~NC~NC" 3 #\a 2 #\b)
  6139. (and (pair? c)
  6140. (eq? (car c) 'make-string)
  6141. (pair? (cdr c))
  6142. (pair? (cddr c))))
  6143. (cdr form))
  6144. (lint-format "perhaps ~A" caller
  6145. (lists->string form
  6146. `(format #f ,(apply string-append (make-list (abs (length (cdr form))) "~NC"))
  6147. ,@(map (lambda (c) (values (cadr c) (caddr c))) (cdr form))))))
  6148. ((not (equal? args (cdr form))) ; (string-append x (string-append y z)) -> (string-append x y z)
  6149. (lint-format "perhaps ~A" caller (lists->string form `(string-append ,@args)))))
  6150. (set! last-checker-line-number line-number))))
  6151. (hash-special 'string-append sp-string-append))
  6152. ;; ---------------- vector-append ----------------
  6153. (let ()
  6154. (define (sp-vector-append caller head form env)
  6155. (unless (= line-number last-checker-line-number)
  6156. (let ((args (remove-all #() (splice-if (lambda (x) (eq? x 'vector-append)) (cdr form)))))
  6157. (cond ((null? args) ; (vector-append) -> #()
  6158. (lint-format "perhaps ~A" caller (lists->string form #())))
  6159. ((null? (cdr args)) ; (vector-append x) -> (copy x)
  6160. (lint-format "perhaps ~A" caller (lists->string form `(copy ,(car args)))))
  6161. ((every? vector? args) ; (vector-append #(1 2) (vector-append #(3))) -> #(1 2 3)
  6162. (lint-format "perhaps ~A" caller (lists->string form (apply vector-append args))))
  6163. ((not (equal? args (cdr form))) ; (vector-append x (vector-append y z)) -> (vector-append x y z)
  6164. (lint-format "perhaps ~A" caller (lists->string form `(vector-append ,@args)))))
  6165. (set! last-checker-line-number line-number))))
  6166. (hash-special 'vector-append sp-vector-append))
  6167. ;; ---------------- cons ----------------
  6168. (let ()
  6169. (define (sp-cons caller head form env)
  6170. (cond ((or (not (= (length form) 3))
  6171. (= last-cons-line-number line-number))
  6172. #f)
  6173. ((and (pair? (caddr form))
  6174. (or (eq? (caaddr form) 'list) ; (cons x (list ...)) -> (list x ...)
  6175. (and (eq? (caaddr form) #_{list})
  6176. (not (tree-member #_{apply_values} (cdaddr form))))))
  6177. (lint-format "perhaps ~A" caller (lists->string form `(list ,(cadr form) ,@(un_{list} (cdaddr form))))))
  6178. ((any-null? (caddr form)) ; (cons x '()) -> (list x)
  6179. (lint-format "perhaps ~A" caller (lists->string form `(list ,(cadr form)))))
  6180. ((not (pair? (caddr form))))
  6181. ((and (pair? (cadr form)) ; (cons (car x) (cdr x)) -> (copy x)
  6182. (let ((x (assq (caadr form)
  6183. '((car cdr #t)
  6184. (caar cdar car) (cadr cddr cdr)
  6185. (caaar cdaar caar) (caadr cdadr cadr) (caddr cdddr cddr) (cadar cddar cdar)
  6186. (cadddr cddddr cdddr) (caaaar cdaaar caaar) (caaadr cdaadr caadr) (caadar cdadar cadar)
  6187. (caaddr cdaddr caddr) (cadaar cddaar cdaar) (cadadr cddadr cdadr) (caddar cdddar cddar)))))
  6188. (and x
  6189. (eq? (cadr x) (caaddr form))
  6190. (caddr x))))
  6191. => (lambda (cfunc)
  6192. (if (and cfunc
  6193. (equal? (cadadr form) (cadr (caddr form)))
  6194. (not (side-effect? (cadadr form) env)))
  6195. (lint-format "perhaps ~A" caller (lists->string form
  6196. (if (symbol? cfunc)
  6197. `(copy (,cfunc ,(cadadr form)))
  6198. `(copy ,(cadadr form))))))))
  6199. ((eq? (caaddr form) 'cons) ; list handled above
  6200. ; (cons a (cons b (cons ...))) -> (list a b ...), input ending in nil of course
  6201. (let loop ((args (list (cadr form))) (chain (caddr form)))
  6202. (if (pair? chain)
  6203. (if (eq? (car chain) 'list)
  6204. (begin
  6205. (lint-format "perhaps ~A" caller (lists->string form `(list ,@(reverse args) ,@(cdr chain))))
  6206. (set! last-cons-line-number line-number))
  6207. (if (and (eq? (car chain) 'cons)
  6208. (pair? (cdr chain))
  6209. (pair? (cddr chain)))
  6210. (if (any-null? (caddr chain))
  6211. (begin
  6212. (lint-format "perhaps ~A" caller (lists->string form `(list ,@(reverse args) ,(cadr chain))))
  6213. (set! last-cons-line-number line-number))
  6214. (if (and (pair? (caddr chain))
  6215. (memq (caaddr chain) '(cons list)))
  6216. (loop (cons (cadr chain) args) (caddr chain)))))))))))
  6217. (hash-special 'cons sp-cons))
  6218. ;; ---------------- append ----------------
  6219. (let ()
  6220. (define (sp-append caller head form env)
  6221. (unless (= line-number last-checker-line-number)
  6222. (set! last-checker-line-number line-number)
  6223. (letrec ((splice-append (lambda (lst)
  6224. (cond ((null? lst)
  6225. ())
  6226. ((not (pair? lst))
  6227. lst)
  6228. ((and (pair? (car lst))
  6229. (eq? (caar lst) 'append))
  6230. (if (null? (cdar lst))
  6231. (if (null? (cdr lst)) ; (append) at end -> () to keep copy intact?
  6232. (list ())
  6233. (splice-append (cdr lst)))
  6234. (append (splice-append (cdar lst)) (splice-append (cdr lst)))))
  6235. ((and (pair? (car lst))
  6236. (eq? (caar lst) 'copy)
  6237. (pair? (cdr lst))
  6238. (null? (cddar lst)))
  6239. (cons (cadar lst) (splice-append (cdr lst))))
  6240. ((or (null? (cdr lst))
  6241. (not (or (any-null? (car lst))
  6242. (and (pair? (car lst))
  6243. (eq? (caar lst) 'list)
  6244. (null? (cdar lst))))))
  6245. (cons (car lst) (splice-append (cdr lst))))
  6246. (else (splice-append (cdr lst)))))))
  6247. (let ((new-args (splice-append (cdr form)))) ; (append '(1) (append '(2) '(3))) -> (append '(1) '(2) '(3))
  6248. (let ((len1 (length new-args))
  6249. (suggestion made-suggestion))
  6250. (if (and (> len1 2)
  6251. (null? (list-ref new-args (- len1 1)))
  6252. (pair? (list-ref new-args (- len1 2)))
  6253. (memq (car (list-ref new-args (- len1 2))) '(list cons append map string->list vector->list make-list)))
  6254. (begin
  6255. (set-cdr! (list-tail new-args (- len1 2)) ())
  6256. (set! len1 (- len1 1))))
  6257. (define (append->list . items)
  6258. (let ((lst (list 'list)))
  6259. (for-each
  6260. (lambda (item)
  6261. (set! lst (append lst (if (eq? (car item) 'list)
  6262. (cdr item)
  6263. (distribute-quote (cadr item))))))
  6264. items)
  6265. lst))
  6266. (if (positive? len1)
  6267. (let ((last (list-ref new-args (- len1 1))))
  6268. ;; (define (f) (append '(1) '(2))) (define a (f)) (set! (a 1) 32) (f) -> '(1 32)
  6269. (if (and (pair? last)
  6270. (eq? (car last) 'quote)
  6271. (pair? (cdr last))
  6272. (pair? (cadr last)))
  6273. (lint-format "append does not copy its last argument, so ~A is dangerous" caller form))))
  6274. (case len1
  6275. ((0) ; (append) -> ()
  6276. (lint-format "perhaps ~A" caller (lists->string form ())))
  6277. ((1) ; (append x) -> x
  6278. (lint-format "perhaps ~A" caller (lists->string form (car new-args))))
  6279. ((2) ; (append (list x) ()) -> (list x)
  6280. (let ((arg2 (cadr new-args))
  6281. (arg1 (car new-args)))
  6282. (cond ((or (any-null? arg2)
  6283. (equal? arg2 '(list))) ; (append x ()) -> (copy x)
  6284. (lint-format "perhaps clearer: ~A" caller (lists->string form `(copy ,arg1))))
  6285. ((null? arg1) ; (append () x) -> x
  6286. (lint-format "perhaps ~A" caller (lists->string form arg2)))
  6287. ((not (pair? arg1)))
  6288. ((and (pair? arg2) ; (append (list x y) '(z)) -> (list x y z) or extensions thereof
  6289. (or (eq? (car arg1) 'list)
  6290. (quoted-undotted-pair? arg1))
  6291. (or (eq? (car arg2) 'list)
  6292. (quoted-undotted-pair? arg2)))
  6293. (lint-format "perhaps ~A" caller (lists->string form (apply append->list new-args))))
  6294. ((and (eq? (car arg1) 'list) ; (append (list x) y) -> (cons x y)
  6295. (pair? (cdr arg1))
  6296. (null? (cddr arg1)))
  6297. (lint-format "perhaps ~A" caller (lists->string form `(cons ,(cadr arg1) ,arg2))))
  6298. ((and (eq? (car arg1) 'list) ; (append (list x y) z) -> (cons x (cons y z))
  6299. (pair? (cdr arg1))
  6300. (pair? (cddr arg1))
  6301. (null? (cdddr arg1)))
  6302. (lint-format "perhaps ~A" caller (lists->string form `(cons ,(cadr arg1) (cons ,(caddr arg1) ,arg2)))))
  6303. ;; not sure about this: reports the un-qq'd form (and never happens)
  6304. ((and (eq? (car arg1) #_{list})
  6305. (not (qq-tree? arg1)))
  6306. (set! last-checker-line-number -1)
  6307. (sp-append caller 'append `(append ,(un_{list} arg1) ,arg2) env))
  6308. ((and (eq? (car arg1) 'vector->list)
  6309. (pair? arg2)
  6310. (eq? (car arg2) 'vector->list))
  6311. (lint-format "perhaps ~A" caller (lists->string form `(vector->list (append ,(cadr arg1) ,(cadr arg2))))))
  6312. ((and (eq? (car arg1) 'quote) ; (append '(x) y) -> (cons 'x y)
  6313. (pair? (cadr arg1))
  6314. (null? (cdadr arg1)))
  6315. (lint-format "perhaps ~A" caller
  6316. (lists->string form
  6317. (if (or (symbol? (caadr arg1))
  6318. (pair? (caadr arg1)))
  6319. `(cons ',(caadr arg1) ,arg2)
  6320. `(cons ,(caadr arg1) ,arg2)))))
  6321. ((not (equal? (cdr form) new-args)) ; (append () '(1 2) 1) -> (append '(1 2) 1)
  6322. (lint-format "perhaps ~A" caller (lists->string form `(append ,@new-args)))))))
  6323. (else
  6324. (cond ((every? (lambda (item)
  6325. (and (pair? item)
  6326. (or (eq? (car item) 'list)
  6327. (quoted-undotted-pair? item))))
  6328. new-args) ; (append '(1) (append '(2) '(3)) '(4)) -> (list 1 2 3 4)
  6329. (lint-format "perhaps ~A" caller (lists->string form (apply append->list new-args))))
  6330. ((and (pair? (car new-args)) ; (append (list x) y (list z)) -> (cons x (append y (list z)))?
  6331. (eq? (caar new-args) 'list)
  6332. (null? (cddar new-args)))
  6333. (lint-format "perhaps ~A" caller (lists->string form `(cons ,(cadar new-args) (append ,@(cdr new-args))))))
  6334. ((let ((n-1 (list-ref new-args (- len1 2))))
  6335. (and (pair? n-1)
  6336. (eq? (car n-1) 'list)
  6337. (pair? (cdr n-1))
  6338. (null? (cddr n-1)))) ; (append x (list y) z) -> (append x (cons y z))
  6339. (lint-format "perhaps ~A" caller
  6340. (lists->string form
  6341. `(append ,@(copy new-args (make-list (- len1 2)))
  6342. (cons ,(cadr (list-ref new-args (- len1 2)))
  6343. ,(list-ref new-args (- len1 1)))))))
  6344. ((not (equal? (cdr form) new-args)) ; (append x y (append)) -> (append x y ())
  6345. (lint-format "perhaps ~A" caller (lists->string form `(append ,@new-args)))))))
  6346. (if (and (= made-suggestion suggestion)
  6347. (not (equal? (cdr form) new-args)))
  6348. (lint-format "perhaps ~A" caller (lists->string form `(append ,@new-args)))))))))
  6349. (hash-special 'append sp-append))
  6350. ;; ---------------- apply ----------------
  6351. (let ()
  6352. (define (sp-apply caller head form env)
  6353. (when (pair? (cdr form))
  6354. (let ((len (length form))
  6355. (suggestion made-suggestion))
  6356. (if (= len 2) ; (apply f) -> (f)
  6357. (lint-format "perhaps ~A" caller (lists->string form (list (cadr form))))
  6358. (if (not (or (<= len 2) ; it might be (apply)...
  6359. (symbol? (cadr form))
  6360. (applicable? (cadr form))))
  6361. (lint-format "~S is not applicable: ~A" caller (cadr form) (truncated-list->string form))
  6362. (let ((happy #f)
  6363. (f (cadr form)))
  6364. (unless (or (<= len 2)
  6365. (any-macro? f env)
  6366. (eq? f 'macroexpand)) ; handled specially (syntactic, not a macro)
  6367. (when (and (symbol? f)
  6368. (not (var-member f env)))
  6369. (let ((func (symbol->value f *e*)))
  6370. (if (procedure? func)
  6371. (let ((ary (arity func)))
  6372. (when (pair? ary) ; (apply real? 1 3 rest)
  6373. (if (> (- len 3) (cdr ary)) ; last apply arg might be var=()
  6374. (lint-format "too many arguments for ~A: ~A" caller f form))
  6375. (if (and (= len 3)
  6376. (= (car ary) 1)
  6377. (= (cdr ary) 1)) ; (apply car x) -> (car (car x))
  6378. (lint-format "perhaps ~A" caller (lists->string form `(,f (car ,(caddr form)))))))))))
  6379. (let ((last-arg (form (- len 1))))
  6380. (if (and (not (list? last-arg))
  6381. (code-constant? last-arg)) ; (apply + 1)
  6382. (lint-format "last argument should be a list: ~A" caller (truncated-list->string form))
  6383. (if (= len 3)
  6384. (let ((args (caddr form))
  6385. (cdr-args (and (pair? (caddr form)) (cdaddr form))))
  6386. (if (identity? f) ; (apply (lambda (x) x) y) -> (car y)
  6387. (lint-format "perhaps (assuming ~A is a list of one element) ~A" caller args
  6388. (lists->string form `(car ,args)))
  6389. (if (simple-lambda? f) ; (apply (lambda (x) (f x)) y) -> (f (car y))
  6390. (lint-format "perhaps (assuming ~A is a list of one element) ~A" caller args
  6391. (lists->string form (tree-subst (list 'car args) (caadr f) (caddr f))))))
  6392. (cond ((eq? f 'list) ; (apply list x) -> x?
  6393. (lint-format "perhaps ~A" caller (lists->string form args)))
  6394. ((any-null? args) ; (apply f ()) -> (f)
  6395. (lint-format "perhaps ~A" caller (lists->string form (list f))))
  6396. ((or (not (pair? args))
  6397. (case (car args)
  6398. ((list) ; (apply f (list a b)) -> (f a b)
  6399. (lint-format "perhaps ~A" caller (lists->string form `(,f ,@cdr-args))))
  6400. ((quote) ; (apply eq? '(a b)) -> (eq? 'a 'b)
  6401. (and (= suggestion made-suggestion)
  6402. (lint-format "perhaps ~A" caller (lists->string form `(,f ,@(distribute-quote (car cdr-args)))))))
  6403. ((cons) ; (apply f (cons a b)) -> (apply f a b)
  6404. (lint-format "perhaps ~A" caller
  6405. (lists->string form
  6406. (if (and (pair? (cadr cdr-args))
  6407. (eq? (caadr cdr-args) 'cons))
  6408. `(apply ,f ,(car cdr-args) ,@(cdadr cdr-args))
  6409. `(apply ,f ,@cdr-args)))))
  6410. ((append) ; (apply f (append (list ...)...)) -> (apply f ... ...)
  6411. (and (pair? (car cdr-args))
  6412. (eq? (caar cdr-args) 'list)
  6413. (lint-format "perhaps ~A" caller
  6414. (lists->string form `(apply ,f ,@(cdar cdr-args)
  6415. ,(if (null? (cdr cdr-args)) ()
  6416. (if (null? (cddr cdr-args)) (cadr cdr-args)
  6417. `(append ,@(cdr cdr-args)))))))))
  6418. ((reverse reverse!) ; (apply vector (reverse x)) -> (reverse (apply vector x))
  6419. (and (memq f '(string vector int-vector float-vector))
  6420. (lint-format "perhaps ~A" caller (lists->string form `(reverse (apply ,f ,(car cdr-args)))))))
  6421. ((make-list) ; (apply string (make-list x y)) -> (make-string x y)
  6422. (if (memq f '(string vector))
  6423. (lint-format "perhaps ~A" caller
  6424. (lists->string form
  6425. `(,(if (eq? f 'string) 'make-string 'make-vector)
  6426. ,@cdr-args)))))
  6427. ((map)
  6428. (case f
  6429. ((string-append) ; (apply string-append (map ...))
  6430. (if (eq? (car cdr-args) 'symbol->string)
  6431. (lint-format "perhaps ~A" caller ; (apply string-append (map symbol->string ...))
  6432. (lists->string form `(format #f "~{~A~}" ,(cadr cdr-args))))
  6433. (if (simple-lambda? (car cdr-args))
  6434. (let ((body (caddar cdr-args)))
  6435. (if (and (pair? body)
  6436. (eq? (car body) 'string-append)
  6437. (= (length body) 3)
  6438. (or (and (string? (cadr body))
  6439. (eq? (caddr body) (caadar cdr-args)))
  6440. (and (string? (caddr body))
  6441. (eq? (cadr body) (caadar cdr-args)))))
  6442. (let ((str (string-append "~{"
  6443. (if (string? (cadr body)) (cadr body) "~A")
  6444. (if (string? (caddr body)) (caddr body) "~A")
  6445. "~}")))
  6446. (lint-format "perhaps ~A" caller
  6447. (lists->string form `(format #f ,str ,(cadr cdr-args))))))))))
  6448. ((string) ; (apply string (map char-downcase x)) -> (string-downcase (apply string x))
  6449. (if (memq (car cdr-args) '(char-upcase char-downcase))
  6450. (lint-format "perhaps, assuming ~A is a list, ~A" caller (cadr cdr-args)
  6451. (lists->string form `(,(if (eq? (car cdr-args) 'char-upcase)
  6452. 'string-upcase 'string-downcase)
  6453. (apply string ,(cadr cdr-args)))))))
  6454. ((append) ; (apply append (map vector->list args)) -> (vector->list (apply append args))
  6455. (and (eq? (car cdr-args) 'vector->list)
  6456. (lint-format "perhaps ~A" caller (lists->string form `(vector->list (apply append ,@(cdr cdr-args)))))))
  6457. (else #f)))
  6458. ;; (apply append (map...)) is very common but changing it to
  6459. ;; (map (lambda (x) (apply values (f x))) ...) from (apply append (map f ...))
  6460. ;; is not an obvious win. The code is more complicated, and currently apply values
  6461. ;; copies its args (as do apply and append -- how many copies are there here?!
  6462. ;; need to check for only one apply values
  6463. ((#_{list}) ; (apply f `(,x ,@z)) -> (apply f x z)
  6464. (let ((last-arg (list-ref args (- (length args) 1))))
  6465. (if (and (pair? last-arg)
  6466. (eq? (car last-arg) #_{apply_values})
  6467. (= (tree-count1 #_{apply_values} args 0) 1))
  6468. (lint-format "perhaps ~A" caller
  6469. (lists->string form
  6470. `(apply ,f
  6471. ,@(copy args (make-list (- (length args) 2)) 1)
  6472. ,(cadr last-arg))))
  6473. (if (not (tree-member #_{apply_values} cdr-args))
  6474. (lint-format "perhaps ~A" caller
  6475. (lists->string form
  6476. `(,f ,@(un_{list} cdr-args)))))))))))))
  6477. (begin ; len > 3
  6478. (when (and (pair? last-arg)
  6479. (eq? (car last-arg) 'list) ; (apply f y z (list a b)) -> (f y z a b)
  6480. (not (hash-table-ref syntaces f))) ; also not any-macro I presume
  6481. (lint-format "perhaps ~A" caller
  6482. (lists->string form
  6483. (append (copy (cdr form) (make-list (- len 2)))
  6484. (cdr last-arg)))))
  6485. ;; can't cleanly go from (apply write o p) to (write o (car p)) since p can be ()
  6486. (when (and (not happy)
  6487. (not (memq f '(define define* define-macro define-macro* define-bacro define-bacro* lambda lambda*)))
  6488. (any-null? last-arg)) ; (apply f ... ()) -> (f ...)
  6489. (lint-format "perhaps ~A" caller (lists->string form `(,f ,@(copy (cddr form) (make-list (- len 3)))))))))))))))
  6490. (if (and (= suggestion made-suggestion)
  6491. (symbol? (cadr form)))
  6492. (let ((ary (arg-arity (cadr form) env)))
  6493. (if (and (pair? ary) ; (apply make-string tcnt initializer) -> (make-string tcnt (car initializer))
  6494. (= (cdr ary) (- len 2)))
  6495. (lint-format "perhaps ~A" caller
  6496. (lists->string form `(,@(copy (cdr form) (make-list (- len 2))) (car ,(list-ref form (- len 1))))))))))))
  6497. (hash-special 'apply sp-apply))
  6498. ;; ---------------- format ----------------
  6499. (let ()
  6500. (define (sp-format caller head form env)
  6501. (if (< (length form) 3)
  6502. (begin
  6503. (cond ((< (length form) 2) ; (format)
  6504. (lint-format "~A has too few arguments: ~A" caller head (truncated-list->string form)))
  6505. ((and (pair? (cadr form)) ; (format (format #f str))
  6506. (eq? (caadr form) 'format))
  6507. (lint-format "redundant format: ~A" caller (truncated-list->string form)))
  6508. ((and (code-constant? (cadr form)) ; (format 1)
  6509. (not (string? (cadr form))))
  6510. (lint-format "format with one argument takes a string: ~A" caller (truncated-list->string form)))
  6511. ((and (string? (cadr form)) ; (format "str") -> str
  6512. (eq? head 'format) ; not snd-display
  6513. (not (char-position #\~ (cadr form))))
  6514. (lint-format "perhaps ~A" caller (lists->string form (cadr form)))))
  6515. env)
  6516. (let ((control-string ((if (string? (cadr form)) cadr caddr) form))
  6517. (args ((if (string? (cadr form)) cddr cdddr) form)))
  6518. (define count-directives
  6519. (let ((format-control-char (let ((chars (make-vector 256 #f)))
  6520. (for-each
  6521. (lambda (c)
  6522. (vector-set! chars (char->integer c) #t))
  6523. '(#\A #\S #\C #\F #\E #\G #\O #\D #\B #\X #\P #\N #\W #\, #\{ #\} #\* #\@
  6524. #\a #\s #\c #\f #\e #\g #\o #\d #\b #\x #\p #\n #\w
  6525. #\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9))
  6526. chars)))
  6527. (lambda (str caller form)
  6528. (let ((curlys 0)
  6529. (dirs 0)
  6530. (pos (char-position #\~ str)))
  6531. (when pos
  6532. (do ((len (length str))
  6533. (tilde-time #t)
  6534. (i (+ pos 1) (+ i 1)))
  6535. ((>= i len)
  6536. (if tilde-time ; (format #f "asdf~")
  6537. (lint-format "~A control string ends in tilde: ~A" caller head (truncated-list->string form))))
  6538. (if tilde-time
  6539. (let ((c (string-ref str i)))
  6540. (when (and (= curlys 0)
  6541. (not (memv c '(#\~ #\T #\t #\& #\% #\^ #\| #\newline #\}))) ; ~* consumes an arg
  6542. (not (call-with-exit
  6543. (lambda (return)
  6544. (do ((k i (+ k 1)))
  6545. ((= k len) #f)
  6546. ;; this can be confused by pad chars in ~T
  6547. (if (not (or (char-numeric? (string-ref str k))
  6548. (char=? (string-ref str k) #\,)))
  6549. (return (char-ci=? (string-ref str k) #\t))))))))
  6550. ;; the possibilities are endless, so I'll stick to the simplest
  6551. (if (not (vector-ref format-control-char (char->integer c))) ; (format #f "~H" 1)
  6552. (lint-format "unrecognized format directive: ~C in ~S, ~S" caller c str form))
  6553. (set! dirs (+ dirs 1))
  6554. ;; ~n so try to figure out how many args are needed (this is not complete)
  6555. (when (char-ci=? c #\n)
  6556. (let ((j (+ i 1)))
  6557. (if (>= j len) ; (format p "~A~A" x)
  6558. (lint-format "missing format directive: ~S" caller str)
  6559. (begin
  6560. ;; if ,n -- add another, if then not T, add another
  6561. (cond ((not (char=? (string-ref str j) #\,)))
  6562. ((>= (+ j 1) len)
  6563. (lint-format "missing format directive: ~S" caller str))
  6564. ((char-ci=? (string-ref str (+ j 1)) #\n)
  6565. (set! dirs (+ dirs 1))
  6566. (set! j (+ j 2)))
  6567. ((char-numeric? (string-ref str (+ j 1)))
  6568. (set! j (+ j 2)))
  6569. (else (set! j (+ j 1))))
  6570. (if (>= j len)
  6571. (lint-format "missing format directive: ~S" caller str)
  6572. (if (not (char-ci=? (string-ref str j) #\t))
  6573. (set! dirs (+ dirs 1)))))))))
  6574. (set! tilde-time #f)
  6575. (case c
  6576. ((#\{) (set! curlys (+ curlys 1)))
  6577. ((#\}) (set! curlys (- curlys 1)))
  6578. ((#\^ #\|)
  6579. (if (zero? curlys) ; (format #f "~^")
  6580. (lint-format "~A has ~~~C outside ~~{~~}?" caller str c))))
  6581. (if (and (< (+ i 2) len)
  6582. (member (substring str i (+ i 3)) '("%~&" "^~^" "|~|" "&~&" "\n~\n") string=?))
  6583. (lint-format "~A in ~A could be ~A" caller ; (format #f "~%~&")
  6584. (substring str (- i 1) (+ i 3))
  6585. str
  6586. (substring str (- i 1) (+ i 1)))))
  6587. (begin
  6588. (set! pos (char-position #\~ str i))
  6589. (if pos
  6590. (begin
  6591. (set! tilde-time #t)
  6592. (set! i pos))
  6593. (set! i len))))))
  6594. (if (not (= curlys 0)) ; (format #f "~{~A" 1)
  6595. (lint-format "~A has ~D unmatched ~A~A: ~A"
  6596. caller head
  6597. (abs curlys)
  6598. (if (positive? curlys) "{" "}")
  6599. (if (> curlys 1) "s" "")
  6600. (truncated-list->string form)))
  6601. dirs))))
  6602. (when (and (eq? head 'format)
  6603. (string? (cadr form))) ; (format "s")
  6604. (lint-format "please include the port argument to format, perhaps ~A" caller `(format () ,@(cdr form))))
  6605. (if (any? all-caps-warning (cdr form))
  6606. (lint-format "There's no need to shout: ~A" caller (truncated-list->string form)))
  6607. (if (and (eq? (cadr form) 't) ; (format t " ")
  6608. (not (var-member 't env)))
  6609. (lint-format "'t in ~A should probably be #t" caller (truncated-list->string form)))
  6610. (if (not (string? control-string))
  6611. (if (not (proper-list? args))
  6612. (lint-format "~S looks suspicious" caller form))
  6613. (let ((ndirs (count-directives control-string caller form))
  6614. (nargs (if (list? args) (length args) 0)))
  6615. (let ((pos (char-position #\null control-string)))
  6616. (if (and pos (< pos (length control-string))) ; (format #f "~a\x00b" x)
  6617. (lint-format "#\\null in a format control string will confuse both lint and format: ~S in ~A" caller control-string form)))
  6618. (if (not (or (= ndirs nargs)
  6619. (tree-memq 'values form)))
  6620. (lint-format "~A has ~A arguments: ~A" ; (format #f "~nT" 1 2)
  6621. caller head
  6622. (if (> ndirs nargs) "too few" "too many")
  6623. (truncated-list->string form))
  6624. (if (and (not (cadr form)) ; (format #f "123")
  6625. (zero? ndirs)
  6626. (not (char-position #\~ control-string)))
  6627. (lint-format "~A could be ~S, (format is a no-op here)" caller (truncated-list->string form) (caddr form))))))
  6628. (when (pair? args)
  6629. (for-each
  6630. (lambda (a)
  6631. (if (pair? a)
  6632. (case (car a)
  6633. ((number->string)
  6634. (if (null? (cddr a)) ; (format #f "~A" (number->string x))
  6635. (lint-format "format arg ~A could be ~A" caller a (cadr a))
  6636. (if (and (pair? (cddr a))
  6637. (integer? (caddr a))
  6638. (memv (caddr a) '(2 8 10 16)))
  6639. (if (= (caddr a) 10)
  6640. (lint-format "format arg ~A could be ~A" caller a (cadr a))
  6641. (lint-format "format arg ~A could use the format directive ~~~A and change the argument to ~A" caller a
  6642. (case (caddr a) ((2) "B") ((8) "O") (else "X"))
  6643. (cadr a))))))
  6644. ((symbol->string) ; (format #f "~A" (symbol->string 'x))
  6645. (lint-format "format arg ~A could be ~A" caller a (cadr a)))
  6646. ((make-string) ; (format #f "~A" (make-string len c))
  6647. (lint-format "format arg ~A could use the format directive ~~NC and change the argument to ... ~A ~A ..." caller a
  6648. (cadr a) (if (char? (caddr a)) (format #f "~W" (caddr a)) (caddr a))))
  6649. ((string-append) ; (format #f "~A" (string-append x y))
  6650. (lint-format "format appends strings, so ~A seems wasteful" caller a)))))
  6651. args)))))
  6652. (hash-special 'format sp-format))
  6653. ;; ---------------- error ----------------
  6654. (let ()
  6655. (define (sp-error caller head form env)
  6656. (if (any? all-caps-warning (cdr form))
  6657. (lint-format "There's no need to shout: ~A" caller (truncated-list->string form))))
  6658. (hash-special 'error sp-error))
  6659. ;; ---------------- sort! ----------------
  6660. (let ()
  6661. (define (sp-sort caller head form env)
  6662. (if (= (length form) 3)
  6663. (let ((func (caddr form)))
  6664. (if (memq func '(= eq? eqv? equal? string=? char=? string-ci=? char-ci=?))
  6665. (lint-format "sort! with ~A may hang: ~A" caller func (truncated-list->string form))
  6666. (if (symbol? func)
  6667. (let ((sig (procedure-signature (symbol->value func))))
  6668. (if (and (pair? sig)
  6669. (not (eq? 'boolean? (car sig)))
  6670. (not (and (pair? (car sig))
  6671. (memq 'boolean? (car sig))))) ; (sort! x abs)
  6672. (lint-format "~A is a questionable sort! function" caller func))))))))
  6673. (hash-special 'sort! sp-sort))
  6674. ;; ---------------- substring ----------------
  6675. (let ()
  6676. (define (sp-substring caller head form env)
  6677. (if (every? code-constant? (cdr form))
  6678. (catch #t
  6679. (lambda ()
  6680. (let ((val (eval form))) ; (substring "abracadabra" 2 7) -> "racad"
  6681. (lint-format "perhaps ~A -> ~S" caller (truncated-list->string form) val)))
  6682. (lambda (type info)
  6683. (lint-format "~A -> ~A" caller (truncated-list->string form) (apply format #f info))))
  6684. (let ((str (cadr form)))
  6685. (when (string? str) ; (substring "++++++" 0 2) -> (make-string 2 #\+)
  6686. (let ((len (length str)))
  6687. (when (and (> len 0)
  6688. (string=? str (make-string len (string-ref str 0))))
  6689. (lint-format "perhaps ~A" caller
  6690. (lists->string form
  6691. (let ((chars (if (null? (cddr form))
  6692. len
  6693. (if (pair? (cdddr form))
  6694. (if (eqv? (caddr form) 0)
  6695. (cadddr form)
  6696. `(- ,(cadddr form) ,(caddr form)))
  6697. `(- ,len ,(caddr form))))))
  6698. `(make-string ,chars ,(string-ref str 0))))))))
  6699. (when (pair? (cddr form))
  6700. (when (null? (cdddr form))
  6701. (when (and (pair? str) ; (substring (substring x 1) 2) -> (substring x 3)
  6702. (eq? (car str) 'substring)
  6703. (null? (cdddr str)))
  6704. (lint-format "perhaps ~A" caller
  6705. (lists->string form
  6706. (if (and (integer? (caddr form))
  6707. (integer? (caddr str)))
  6708. `(substring ,(cadr str) ,(+ (caddr str) (caddr form)))
  6709. `(substring ,(cadr str) (+ ,(caddr str) ,(caddr form)))))))
  6710. ;; end indices are complicated -- since this rarely happens, not worth the trouble
  6711. (if (eqv? (caddr form) 0) ; (substring x 0) -> (copy x)
  6712. (lint-format "perhaps clearer: ~A" caller (lists->string form `(copy ,str)))))
  6713. (when (pair? (cdddr form))
  6714. (let ((end (cadddr form)))
  6715. (if (equal? (caddr form) end) ; (substring x (+ y 1) (+ y 1)) is ""
  6716. (lint-format "leaving aside errors, ~A is \"\"" caller form))
  6717. (when (and (pair? str)
  6718. (eqv? (caddr form) 0)
  6719. (eq? (car str) 'string-append)
  6720. (= (length str) 3))
  6721. (let ((in-arg2 (caddr str)))
  6722. (if (and (pair? in-arg2) ; (substring (string-append str (make-string len #\space)) 0 len) -> (copy str (make-string len #\space))
  6723. (eq? (car in-arg2) 'make-string)
  6724. (equal? (cadddr form) (cadr in-arg2)))
  6725. (lint-format "perhaps ~A" caller
  6726. (lists->string form `(copy ,(cadr str) (make-string ,(cadddr form) ,(caddr in-arg2))))))))
  6727. (if (and (pair? end) ; (substring x start (length|string-length x)) -> (substring s start)
  6728. (memq (car end) '(string-length length))
  6729. (equal? (cadr end) str))
  6730. (lint-format "perhaps ~A" caller (lists->string form (copy form (make-list 3))))
  6731. (when (symbol? end)
  6732. (let ((v (var-member end env)))
  6733. (if (and (var? v)
  6734. (equal? `(string-length ,str) (var-initial-value v))
  6735. (not (any? (lambda (p)
  6736. (set!? p env))
  6737. (var-history v)))) ; if len is still (string-length x), (substring x 1 len) -> (substring x 1)
  6738. (lint-format "perhaps, if ~A is still ~A, ~A" caller end (var-initial-value v)
  6739. (lists->string form (copy form (make-list 3))))))))))))))
  6740. (hash-special 'substring sp-substring))
  6741. ;; ---------------- list, *vector ----------------
  6742. (let ((seq-maker (lambda (seq)
  6743. (cdr (assq seq '((list . make-list)
  6744. (vector . make-vector)
  6745. (float-vector . make-float-vector)
  6746. (int-vector . make-int-vector)
  6747. (byte-vector . make-byte-vector))))))
  6748. (seq-default (lambda (seq)
  6749. (cdr (assq seq '((list . #f)
  6750. (vector . #<unspecified>)
  6751. (float-vector . 0.0)
  6752. (int-vector . 0)
  6753. (byte-vector . 0)))))))
  6754. (define (sp-list caller head form env)
  6755. (let ((len (length form))
  6756. (val (and (pair? (cdr form))
  6757. (cadr form))))
  6758. (when (and (> len 4)
  6759. (every? (lambda (a) (equal? a val)) (cddr form)))
  6760. (if (code-constant? val) ; (vector 12 12 12 12 12 12) -> (make-vector 6 12)
  6761. (lint-format "perhaps ~A~A" caller
  6762. (lists->string form
  6763. (if (eqv? (seq-default head) val)
  6764. `(,(seq-maker head) ,(- len 1))
  6765. `(,(seq-maker head) ,(- len 1) ,val)))
  6766. (if (and (sequence? val)
  6767. (not (null? val)))
  6768. (format #f "~%~NCor wrap (copy ~S) in a function and call that ~A times"
  6769. lint-left-margin #\space
  6770. val (- len 1))
  6771. ""))
  6772. (if (pair? val)
  6773. (if (or (side-effect? val env)
  6774. (hash-table-ref makers (car val)))
  6775. (if (> (tree-leaves val) 3)
  6776. ;; I think we need to laboriously repeat the function call here:
  6777. ;; (let ((a 1) (b 2) (c 3))
  6778. ;; (define f (let ((ctr 0)) (lambda (x y z) (set! ctr (+ ctr 1)) (+ x y ctr (* 2 z)))))
  6779. ;; (list (f a b c) (f a b c) (f a b c) (f a b c))
  6780. ;; so (apply list (make-list 4 (_1_))) or variants thereof fail
  6781. ;; (eval (append '(list) (make-list 4 '(_1_))))
  6782. ;; works, but it's too ugly.
  6783. (lint-format "perhaps ~A" caller
  6784. (lists->string form
  6785. `(let ((_1_ (lambda () ,val)))
  6786. (,head ,@(make-list (- len 1) '(_1_)))))))
  6787. ;; if seq copy else
  6788. (lint-format "perhaps ~A" caller ; (vector (car x) (car x) (car x) (car x)) -> (make-vector 4 (car x))
  6789. (lists->string form `(,(seq-maker head) ,(- len 1) ,val)))))))))
  6790. (for-each (lambda (f) (hash-special f sp-list)) '(list vector int-vector float-vector byte-vector)))
  6791. ;; ---------------- list-tail ----------------
  6792. (let ()
  6793. (define (sp-list-tail caller head form env)
  6794. (if (= (length form) 3)
  6795. (if (eqv? (caddr form) 0) ; (list-tail x 0) -> x
  6796. (lint-format "perhaps ~A" caller (lists->string form (cadr form)))
  6797. (if (and (pair? (cadr form))
  6798. (eq? (caadr form) 'list-tail))
  6799. (lint-format "perhaps ~A" caller ; (list-tail (list-tail x 1) 2) -> (list-tail x 3)
  6800. (lists->string form
  6801. (if (and (integer? (caddr form))
  6802. (integer? (caddr (cadr form))))
  6803. `(list-tail ,(cadadr form) ,(+ (caddr (cadr form)) (caddr form)))
  6804. `(list-tail ,(cadadr form) (+ ,(caddr (cadr form)) ,(caddr form))))))))))
  6805. (hash-special 'list-tail sp-list-tail))
  6806. ;; ---------------- eq? ----------------
  6807. (let ()
  6808. (define (sp-eq? caller head form env)
  6809. (if (< (length form) 3) ; (eq?)
  6810. (lint-format "eq? needs 2 arguments: ~A" caller (truncated-list->string form))
  6811. (let* ((arg1 (cadr form))
  6812. (arg2 (caddr form))
  6813. (eq1 (eqf arg1 env))
  6814. (eq2 (eqf arg2 env))
  6815. (specific-op (and (eq? (cadr eq1) (cadr eq2))
  6816. (not (memq (cadr eq1) '(eqv? equal?)))
  6817. (cadr eq1))))
  6818. (eval-constant-expression caller form)
  6819. (if (or (eq? (car eq1) 'equal?)
  6820. (eq? (car eq2) 'equal?)) ; (eq? #(0) #(0))
  6821. (lint-format "eq? should be equal?~A in ~S" caller (if specific-op (format #f " or ~A" specific-op) "") form)
  6822. (if (or (eq? (car eq1) 'eqv?)
  6823. (eq? (car eq2) 'eqv?)) ; (eq? x 1.5)
  6824. (lint-format "eq? should be eqv?~A in ~S" caller (if specific-op (format #f " or ~A" specific-op) "") form)))
  6825. (let ((expr 'unset))
  6826. (cond ((or (not arg1) ; (eq? #f x) -> (not x)
  6827. (quoted-not? arg1))
  6828. (set! expr (simplify-boolean `(not ,arg2) () () env)))
  6829. ((or (not arg2) ; (eq? x #f) -> (not x)
  6830. (quoted-not? arg2))
  6831. (set! expr (simplify-boolean `(not ,arg1) () () env)))
  6832. ((and (any-null? arg1) ; (eq? () x) -> (null? x)
  6833. (not (code-constant? arg2)))
  6834. (set! expr (or (equal? arg2 '(list)) ; (eq? () (list)) -> #t
  6835. `(null? ,arg2))))
  6836. ((and (any-null? arg2) ; (eq? x ()) -> (null? x)
  6837. (not (code-constant? arg1)))
  6838. (set! expr (or (equal? arg1 '(list))
  6839. `(null? ,arg1))))
  6840. ((and (eq? arg1 #t) ; (eq? #t <boolean-expr>) -> boolean-expr
  6841. (pair? arg2)
  6842. (eq? (return-type (car arg2) env) 'boolean?))
  6843. (set! expr arg2))
  6844. ((and (eq? arg2 #t) ; (eq? <boolean-expr> #t) -> boolean-expr
  6845. (pair? arg1)
  6846. (eq? (return-type (car arg1) env) 'boolean?))
  6847. (set! expr arg1)))
  6848. (if (not (eq? expr 'unset)) ; (eq? x '()) -> (null? x)
  6849. (lint-format "perhaps ~A" caller (lists->string form expr)))))))
  6850. (hash-special 'eq? sp-eq?))
  6851. ;; ---------------- eqv? equal? ----------------
  6852. (let ()
  6853. (define (sp-eqv? caller head form env)
  6854. (define (useless-copy? a)
  6855. (and (pair? a)
  6856. (memq (car a) '(copy string-copy vector-copy list-copy))
  6857. (null? (cddr a))))
  6858. (if (< (length form) 3)
  6859. (lint-format "~A needs 2 arguments: ~A" caller head (truncated-list->string form))
  6860. (let* ((arg1 (cadr form))
  6861. (arg2 (caddr form))
  6862. (eq1 (eqf arg1 env))
  6863. (eq2 (eqf arg2 env))
  6864. (specific-op (and (eq? (cadr eq1) (cadr eq2))
  6865. (not (memq (cadr eq1) '(eq? eqv? equal?)))
  6866. (cadr eq1))))
  6867. (eval-constant-expression caller form)
  6868. (if (or (useless-copy? arg1)
  6869. (useless-copy? arg2)) ; (equal? (vector-copy #(a b c)) #(a b c)) -> (equal? #(a b c) #(a b c))
  6870. (lint-format "perhaps ~A" caller
  6871. (lists->string form
  6872. `(,head ,(if (useless-copy? arg1) (cadr arg1) arg1)
  6873. ,(if (useless-copy? arg2) (cadr arg2) arg2)))))
  6874. (if (and (string? (cadr form))
  6875. (= (length (cadr form)) 1))
  6876. (let ((s2 (caddr form)))
  6877. (if (pair? s2)
  6878. (if (eq? (car s2) 'string) ; (equal? "[" (string r)) -> (char=? #\[ r)
  6879. (lint-format "perhaps ~A" caller
  6880. (lists->string form `(char=? ,(string-ref (cadr form) 0) ,(cadr s2))))
  6881. (if (and (eq? (car s2) 'substring)
  6882. (= (length s2) 4) ; (equal? "^" (substring s 0 1)) -> (char=? #\^ (string-ref s 0))
  6883. (eqv? (list-ref s2 2) 0)
  6884. (eqv? (list-ref s2 3) 1))
  6885. (lint-format "perhaps ~A" caller
  6886. (lists->string form `(char=? ,(string-ref (cadr form) 0) (string-ref ,(cadr s2) 0)))))))))
  6887. (if (and (not (eq? (cadr eq1) (cadr eq2))) ; (eqv? ":" (string-ref s 0))
  6888. (memq (cadr eq1) '(char=? string=?))
  6889. (memq (cadr eq2) '(char=? string=?)))
  6890. (lint-format "this can't be right: ~A" caller form))
  6891. ;; (equal? a (list b)) and equivalents happens a lot, but is the extra consing worse than
  6892. ;; (and (pair? a) (equal? (car a) b) (null? (cdr a))) -- code readability seems more important here
  6893. (cond ((or (eq? (car eq1) 'equal?)
  6894. (eq? (car eq2) 'equal?))
  6895. (if (eq? head 'equal?)
  6896. (if specific-op ; equal? could be string=? in (equal? (string x) (string-append y z))
  6897. (lint-format "~A could be ~A in ~S" caller head specific-op form))
  6898. (lint-format "~A should be equal?~A in ~S" caller head
  6899. (if specific-op (format #f " or ~A" specific-op) "")
  6900. form)))
  6901. ((or (eq? (car eq1) 'eqv?)
  6902. (eq? (car eq2) 'eqv?))
  6903. (if (eq? head 'eqv?)
  6904. (if specific-op ; (eqv? (integer->char x) #\null)
  6905. (lint-format "~A could be ~A in ~S" caller head specific-op form))
  6906. (lint-format "~A ~A be eqv?~A in ~S" caller head
  6907. (if (eq? head 'eq?) "should" "could")
  6908. (if specific-op (format #f " or ~A" specific-op) "")
  6909. form)))
  6910. ((not (or (eq? (car eq1) 'eq?)
  6911. (eq? (car eq2) 'eq?))))
  6912. ((not (and arg1 arg2)) ; (eqv? x #f) -> (not x)
  6913. (lint-format "~A could be not: ~A" caller head (lists->string form `(not ,(or arg1 arg2)))))
  6914. ((or (any-null? arg1)
  6915. (any-null? arg2)) ; (eqv? x ()) -> (null? x)
  6916. (lint-format "~A could be null?: ~A" caller head
  6917. (lists->string form
  6918. (if (any-null? arg1)
  6919. `(null? ,arg2)
  6920. `(null? ,arg1)))))
  6921. (else ; (eqv? x 'a)
  6922. (lint-format "~A could be eq?~A in ~S" caller head
  6923. (if specific-op (format #f " or ~A" specific-op) "")
  6924. form))))))
  6925. (hash-special 'eqv? sp-eqv?)
  6926. (hash-special 'equal? sp-eqv?))
  6927. ;; ---------------- map for-each ----------------
  6928. (let ()
  6929. (define (sp-map caller head form env)
  6930. (let* ((len (length form))
  6931. (args (- len 2)))
  6932. (if (< len 3) ; (map (lambda (v) (vector-ref v 0)))
  6933. (lint-format "~A missing argument~A in: ~A"
  6934. caller head
  6935. (if (= len 2) "" "s")
  6936. (truncated-list->string form))
  6937. (let ((func (cadr form))
  6938. (ary #f))
  6939. ;; if zero or one args, the map/for-each is either a no-op or a function call
  6940. (if (any? any-null? (cddr form)) ; (map abs ())
  6941. (lint-format "this ~A has no effect (null arg)" caller (truncated-list->string form))
  6942. (if (and (not (tree-memq 'values form)) ; e.g. flatten in s7.html
  6943. (any? (lambda (p)
  6944. (and (pair? p)
  6945. (case (car p)
  6946. ((quote)
  6947. (and (pair? (cadr p))
  6948. (null? (cdadr p))))
  6949. ((list)
  6950. (null? (cddr p)))
  6951. ((cons)
  6952. (any-null? (caddr p)))
  6953. (else #f))))
  6954. (cddr form))) ; (for-each display (list a)) -> (display a)
  6955. (lint-format "perhaps ~A" caller
  6956. (lists->string form
  6957. (let ((args (map (lambda (a)
  6958. (if (pair? a)
  6959. (case (car a)
  6960. ((list cons)
  6961. (cadr a)) ; slightly inaccurate
  6962. ((quote)
  6963. (caadr a))
  6964. (else `(,a 0))) ; not car -- might not be a list
  6965. `(,a 0))) ; but still not right -- arg might be a hash-table
  6966. (cddr form))))
  6967. (if (eq? head 'for-each)
  6968. `(,(cadr form) ,@args)
  6969. `(list (,(cadr form) ,@args))))))))
  6970. ;; 2 happens a lot, but introduces evaluation order quibbles
  6971. ;; we used to check for values if list arg -- got 4 hits!
  6972. (if (and (symbol? func)
  6973. (procedure? (symbol->value func *e*)))
  6974. (begin
  6975. (set! ary (arity (symbol->value func *e*)))
  6976. (if (and (eq? head 'map)
  6977. (hash-table-ref no-side-effect-functions func)
  6978. (= len 3)
  6979. (pair? (caddr form))
  6980. (or (eq? (caaddr form) 'quote)
  6981. (and (eq? (caaddr form) 'list)
  6982. (every? code-constant? (cdaddr form)))))
  6983. (catch #t
  6984. (lambda () ; (map symbol->string '(a b c d)) -> '("a" "b" "c" "d")
  6985. (let ((val (eval form)))
  6986. (lint-format "perhaps ~A" caller (lists->string form (list 'quote val)))))
  6987. (lambda args #f))))
  6988. (when (and (pair? func)
  6989. (memq (car func) '(lambda lambda*)))
  6990. (if (pair? (cadr func))
  6991. (let ((arglen (length (cadr func))))
  6992. (set! ary (if (eq? (car func) 'lambda)
  6993. (if (negative? arglen)
  6994. (cons (abs arglen) 512000)
  6995. (cons arglen arglen))
  6996. (cons 0 (if (or (negative? arglen)
  6997. (memq :rest (cadr func)))
  6998. 512000 arglen))))))
  6999. (if (= len 3)
  7000. (let ((body (cddr func))) ; (map (lambda (a) #f) x) -> (make-list (abs (length x)) #f)
  7001. (if (and (null? (cdr body))
  7002. (code-constant? (car body)))
  7003. (lint-format "perhaps ~A" caller
  7004. (lists->string form
  7005. `(make-list (abs (length ,(caddr form))) ,(car body)))))))))
  7006. (if (pair? ary)
  7007. (if (< args (car ary)) ; (map (lambda (a b) a) '(1 2))
  7008. (lint-format "~A has too few arguments in: ~A"
  7009. caller head
  7010. (truncated-list->string form))
  7011. (if (> args (cdr ary)) ; (map abs '(1 2) '(3 4))
  7012. (lint-format "~A has too many arguments in: ~A"
  7013. caller head
  7014. (truncated-list->string form)))))
  7015. (for-each
  7016. (lambda (obj)
  7017. (if (and (pair? obj)
  7018. (memq (car obj) '(vector->list string->list let->list)))
  7019. (lint-format* caller ; (vector->list #(1 2)) could be simplified to: #(1 2)
  7020. (truncated-list->string obj)
  7021. " could be simplified to: "
  7022. (truncated-list->string (cadr obj))
  7023. (string-append " ; (" (symbol->string head) " accepts non-list sequences)"))))
  7024. (cddr form))
  7025. (when (eq? head 'map)
  7026. (when (and (memq func '(char-downcase char-upcase))
  7027. (pair? (caddr form)) ; (map char-downcase (string->list str)) -> (string->list (string-downcase str))
  7028. (eq? (caaddr form) 'string->list))
  7029. (lint-format "perhaps ~A" caller (lists->string form `(string->list (,(if (eq? func 'char-upcase) 'string-upcase 'string-downcase)
  7030. ,(cadr (caddr form)))))))
  7031. (when (identity? func) ; to check f here as var is more work ; (map (lambda (x) x) lst) -> lst
  7032. (lint-format "perhaps ~A" caller (lists->string form (caddr form)))))
  7033. (let ((arg1 (caddr form)))
  7034. (when (and (pair? arg1)
  7035. (memq (car arg1) '(cdr cddr cdddr cddddr list-tail))
  7036. (pair? (cdr arg1))
  7037. (pair? (cadr arg1))
  7038. (memq (caadr arg1) '(string->list vector->list)))
  7039. (let ((string-case (eq? (caadr arg1) 'string->list))
  7040. (len-diff (if (eq? (car arg1) 'list-tail)
  7041. (caddr arg1)
  7042. (cdr-count (car arg1))))) ; (cdr (vector->list v)) -> (make-shared-vector v (- (length v) 1) 1)
  7043. (lint-format "~A accepts ~A arguments, so perhaps ~A" caller head
  7044. (if string-case 'string 'vector)
  7045. (lists->string arg1 (if string-case
  7046. `(substring ,(cadadr arg1) ,len-diff)
  7047. `(make-shared-vector ,(cadadr arg1) (- (length ,(cadadr arg1)) ,len-diff) ,len-diff)))))))
  7048. (when (and (eq? head 'for-each)
  7049. (pair? (cadr form))
  7050. (eq? (caadr form) 'lambda)
  7051. (pair? (cdadr form)) ; (for-each (lambda (x) (+ (abs x) 1)) lst)
  7052. (not (any? (lambda (x) (side-effect? x env)) (cddadr form))))
  7053. (lint-format "pointless for-each: ~A" caller (truncated-list->string form)))
  7054. (when (= args 1)
  7055. (let ((seq (caddr form)))
  7056. (when (pair? seq)
  7057. (case (car seq)
  7058. ((cons) ; (for-each display (cons msgs " "))
  7059. (if (and (pair? (cdr seq))
  7060. (pair? (cddr seq))
  7061. (code-constant? (caddr seq)))
  7062. (lint-format "~A will ignore ~S in ~A" caller head (caddr seq) seq)))
  7063. ((map)
  7064. (when (= (length seq) 3)
  7065. ;; a toss-up -- probably faster to combine funcs here, and easier to read?
  7066. ;; but only if first arg is only used once in first func, and everything is simple (one-line or symbol)
  7067. (let* ((seq-func (cadr seq))
  7068. (arg-name (find-unique-name func seq-func)))
  7069. (if (symbol? func) ; (map f (map g h)) -> (map (lambda (_1_) (f (g _1_))) h) -- dubious
  7070. (if (symbol? seq-func)
  7071. (lint-format "perhaps ~A" caller
  7072. (lists->string form `(,head (lambda (,arg-name)
  7073. (,func (,seq-func ,arg-name)))
  7074. ,(caddr seq))))
  7075. (if (simple-lambda? seq-func)
  7076. ;; (map f (map (lambda (x) (g x)) h)) -> (map (lambda (x) (f (g x))) h)
  7077. (lint-format "perhaps ~A" caller
  7078. (lists->string form `(,head (lambda (,arg-name)
  7079. (,func ,(tree-subst arg-name (caadr seq-func) (caddr seq-func))))
  7080. ,(caddr seq))))))
  7081. (if (less-simple-lambda? func)
  7082. (if (symbol? seq-func)
  7083. ;; (map (lambda (x) (f x)) (map g h)) -> (map (lambda (x) (f (g x))) h)
  7084. (lint-format "perhaps ~A" caller
  7085. (lists->string form `(,head (lambda (,arg-name)
  7086. ,@(tree-subst (list seq-func arg-name) (caadr func) (cddr func)))
  7087. ,(caddr seq))))
  7088. (if (simple-lambda? seq-func)
  7089. ;; (map (lambda (x) (f x)) (map (lambda (x) (g x)) h)) -> (map (lambda (x) (f (g x))) h)
  7090. (lint-format "perhaps ~A" caller
  7091. (lists->string form `(,head (lambda (,arg-name)
  7092. ,@(tree-subst (tree-subst arg-name (caadr seq-func) (caddr seq-func))
  7093. (caadr func) (cddr func)))
  7094. ,(caddr seq)))))))))))))
  7095. ;; repetitive code...
  7096. (when (eq? head 'for-each) ; args = 1 above ; (for-each display (list a)) -> (format () "~A" a)
  7097. (let ((func (cadr form)))
  7098. (if (memq func '(display write newline write-char write-string))
  7099. (lint-format "perhaps ~A" caller
  7100. (if (and (pair? seq)
  7101. (memq (car seq) '(list quote)))
  7102. (let ((op (if (eq? func 'write) "~S" "~A"))
  7103. (len (- (length seq) 1)))
  7104. (lists->string form `(format () ,(do ((i 0 (+ i 1))
  7105. (str ""))
  7106. ((= i len) str)
  7107. (set! str (string-append str op)))
  7108. ,@(cdr seq))))
  7109. (let ((op (if (eq? func 'write) "~{~S~}" "~{~A~}")))
  7110. (lists->string form `(format () ,op ,seq)))))
  7111. (when (and (pair? func)
  7112. (eq? (car func) 'lambda))
  7113. (let ((body (cddr func)))
  7114. (let ((op (write-port (car body)))
  7115. (larg (and (pair? (cadr func))
  7116. (caadr func))))
  7117. (when (and (symbol? larg)
  7118. (null? (cdadr func)) ; just one arg (one sequence to for-each) for now
  7119. (every? (lambda (x)
  7120. (and (pair? x)
  7121. (memq (car x) '(display write newline write-char write-string))
  7122. (or (eq? (car x) 'newline)
  7123. (eq? (cadr x) larg)
  7124. (string? (cadr x))
  7125. (eqv? (cadr x) #\space)
  7126. (and (pair? (cadr x))
  7127. (pair? (cdadr x))
  7128. (eq? (caadr x) 'number->string)
  7129. (eq? (cadadr x) larg)))
  7130. (eq? (write-port x) op)))
  7131. body))
  7132. ;; (for-each (lambda (x) (display x) (write-char #\space)) msg)
  7133. ;; (for-each (lambda (elt) (display elt)) lst)
  7134. (let ((ctrl-string "")
  7135. (arg-ctr 0))
  7136. (define* (gather-format str (arg :unset))
  7137. (set! ctrl-string (string-append ctrl-string str)))
  7138. (for-each
  7139. (lambda (d)
  7140. (if (or (memq larg d)
  7141. (and (pair? (cdr d))
  7142. (pair? (cadr d))
  7143. (memq larg (cadr d))))
  7144. (set! arg-ctr (+ arg-ctr 1)))
  7145. (gather-format (display->format d)))
  7146. body)
  7147. (when (= arg-ctr 1) ; (for-each (lambda (x) (display x)) args) -> (format () "~{~A~}" args)
  7148. (lint-format "perhaps ~A" caller
  7149. (lists->string form `(format ,op ,(string-append "~{" ctrl-string "~}") ,seq)))))))))
  7150. )))))))))
  7151. (for-each (lambda (f)
  7152. (hash-special f sp-map))
  7153. '(map for-each)))
  7154. ;; ---------------- magnitude ----------------
  7155. (let ()
  7156. (define (sp-magnitude caller head form env)
  7157. (if (and (= (length form) 2) ; (magnitude 2/3)
  7158. (memq (->lint-type (cadr form)) '(integer? rational? real?)))
  7159. (lint-format "perhaps use abs here: ~A" caller form)))
  7160. (hash-special 'magnitude sp-magnitude))
  7161. ;; ---------------- open-input-file open-output-file ----------------
  7162. (let ()
  7163. (define (sp-open-input-file caller head form env)
  7164. (if (and (pair? (cdr form))
  7165. (pair? (cddr form))
  7166. (string? (caddr form)) ; (open-output-file x "fb+")
  7167. (not (memv (string-ref (caddr form) 0) '(#\r #\w #\a)))) ; b + then e m c x if gcc
  7168. (lint-format "unexpected mode: ~A" caller form)))
  7169. (for-each (lambda (f)
  7170. (hash-special f sp-open-input-file))
  7171. '(open-input-file open-output-file)))
  7172. ;; ---------------- values ----------------
  7173. (let ()
  7174. (define (sp-values caller head form env)
  7175. (cond ((member 'values (cdr form) (lambda (a b)
  7176. (and (pair? b) ; (values 2 (values 3 4) 5) -> (values 2 3 4 5)
  7177. (eq? (car b) 'values))))
  7178. (lint-format "perhaps ~A" caller (lists->string form `(values ,@(splice-if (lambda (x) (eq? x 'values)) (cdr form))))))
  7179. ((= (length form) 2)
  7180. (lint-format "perhaps ~A" caller
  7181. (lists->string form ; (values ({list} 'x ({apply_values} y))) -> (cons 'x y)
  7182. (if (and (pair? (cadr form))
  7183. (eq? (caadr form) #_{list})
  7184. (not (qq-tree? (cadr form))))
  7185. (un_{list} (cadr form))
  7186. (cadr form)))))
  7187. ((and (assq #_{list} (cdr form))
  7188. (not (any? (lambda (a)
  7189. (and (pair? a)
  7190. (memq (car a) '(#_{list} #_{apply_values}))
  7191. (qq-tree? a)))
  7192. (cdr form))))
  7193. (lint-format "perhaps ~A" caller
  7194. (lists->string form ; (values ({list} 'x y) a) -> (values (list 'x y) a)
  7195. `(values ,@(map (lambda (a)
  7196. (if (and (pair? a)
  7197. (eq? (car a) #_{list}))
  7198. (un_{list} a)
  7199. a))
  7200. (cdr form))))))))
  7201. (hash-special 'values sp-values))
  7202. ;; ---------------- call-with-values ----------------
  7203. (let ()
  7204. (define (sp-call/values caller head form env) ; (call/values p c) -> (c (p))
  7205. (when (= (length form) 3)
  7206. (let ((producer (cadr form))
  7207. (consumer (caddr form)))
  7208. (let* ((produced-values (mv-range producer env))
  7209. (consumed-values (and produced-values
  7210. (or (and (symbol? consumer)
  7211. (arg-arity consumer env))
  7212. (and (pair? consumer)
  7213. (eq? (car consumer) 'lambda)
  7214. (pair? (cadr consumer))
  7215. (let ((len (length (cadr consumer))))
  7216. (if (negative? len)
  7217. (cons (abs len) (cdr (arity +))) ; 536870912 = MAX_ARITY in s7.c
  7218. (cons len len))))))))
  7219. (if (and consumed-values
  7220. (or (> (car consumed-values) (car produced-values))
  7221. (< (cdr consumed-values) (cadr produced-values))))
  7222. (let ((clen ((if (> (car consumed-values) (car produced-values)) car cdr) consumed-values)))
  7223. (lint-format "call-with-values consumer ~A wants ~D value~P, but producer ~A returns ~A"
  7224. caller
  7225. (truncated-list->string consumer)
  7226. clen clen
  7227. (truncated-list->string producer)
  7228. ((if (> (car consumed-values) (car produced-values)) car cadr) produced-values)))))
  7229. (cond ((not (pair? producer)) ; (call-with-values log c)
  7230. (if (and (symbol? producer)
  7231. (not (memq (return-type producer ()) '(#t #f values))))
  7232. (lint-format "~A does not return multiple values" caller producer)
  7233. (lint-format "perhaps ~A" caller (lists->string form `(,consumer (,producer))))))
  7234. ((not (eq? (car producer) 'lambda)) ; (call-with-values (eval p env) (eval c env)) -> ((eval c env) ((eval p env)))
  7235. (lint-format "perhaps ~A" caller (lists->string form `(,consumer (,producer)))))
  7236. ((pair? (cadr producer)) ; (call-with-values (lambda (x) 0) list)
  7237. (lint-format "~A requires too many arguments" caller (truncated-list->string producer)))
  7238. ((symbol? (cadr producer)) ; (call-with-values (lambda x 0) list)
  7239. (lint-format "~A's parameter ~A will always be ()" caller (truncated-list->string producer) (cadr producer)))
  7240. ((and (pair? (cddr producer)) ; (call-with-values (lambda () (read-char p)) cons)
  7241. (null? (cdddr producer))) ; (call-with-values (lambda () (values 1 2 3)) list) -> (list 1 2 3)
  7242. (let ((body (caddr producer)))
  7243. (if (or (code-constant? body)
  7244. (and (pair? body)
  7245. (symbol? (car body))
  7246. (not (memq (return-type (car body) ()) '(#t #f values)))))
  7247. (lint-format "~A does not return multiple values" caller body)
  7248. (lint-format "perhaps ~A" caller
  7249. (lists->string form
  7250. (if (and (pair? body)
  7251. (eq? (car body) 'values))
  7252. `(,consumer ,@(cdr body))
  7253. `(,consumer ,body)))))))
  7254. (else (lint-format "perhaps ~A" caller (lists->string form `(,consumer (,producer)))))))))
  7255. (hash-special 'call-with-values sp-call/values))
  7256. ;; ---------------- multiple-value-bind ----------------
  7257. (let ()
  7258. (define (sp-mvb caller head form env)
  7259. (when (>= (length form) 4)
  7260. (let ((vars (cadr form))
  7261. (producer (caddr form))
  7262. (body (cdddr form)))
  7263. (if (null? vars)
  7264. (lint-format "this multiple-value-bind is pointless; perhaps ~A" caller
  7265. (lists->string form
  7266. (if (side-effect? producer env)
  7267. `(begin ,producer ,@body)
  7268. (if (null? (cdr body))
  7269. (car body)
  7270. `(begin ,@body)))))
  7271. (unless (symbol? vars) ; else any number of values is ok
  7272. (let ((vals (mv-range producer env)) ; (multiple-value-bind (a b) (values 1 2 3) b)
  7273. (args (length vars)))
  7274. (if (and (pair? vals)
  7275. (not (<= (car vals) args (cadr vals))))
  7276. (lint-format "multiple-value-bind wants ~D values, but ~A returns ~A"
  7277. caller args
  7278. (truncated-list->string producer)
  7279. ((if (< args (car vals)) car cadr) vals)))
  7280. (if (and (pair? producer) ; (multiple-value-bind (a b) (f) b) -> ((lambda (a b) b) (f))
  7281. (symbol? (car producer))
  7282. (not (memq (return-type (car producer) ()) '(#t #f values))))
  7283. (lint-format "~A does not return multiple values" caller (car producer))
  7284. (lint-format "perhaps ~A" caller
  7285. (lists->string form
  7286. (if (and (null? (cdr body))
  7287. (pair? (car body))
  7288. (equal? vars (cdar body))
  7289. (defined? (caar body))
  7290. (equal? (arity (symbol->value (caar body))) (cons args args)))
  7291. `(,(caar body) ,producer)
  7292. `((lambda ,vars ,@body) ,producer)))))))))))
  7293. (hash-special 'multiple-value-bind sp-mvb))
  7294. ;; ---------------- let-values ----------------
  7295. (let ()
  7296. (define (sp-let-values caller head form env)
  7297. (if (and (pair? (cdr form))
  7298. (pair? (cadr form)))
  7299. (if (null? (cdadr form)) ; just one set of vars
  7300. (let ((call (caadr form)))
  7301. (if (and (pair? call)
  7302. (pair? (cdr call)))
  7303. (lint-format "perhaps ~A" caller ; (let-values (((x) (values 1))) x) -> ((lambda (x) x) (values 1))
  7304. (lists->string form
  7305. `((lambda ,(car call)
  7306. ,@(cddr form))
  7307. ,(cadr call))))))
  7308. (if (every? pair? (cadr form))
  7309. (lint-format "perhaps ~A" caller ; (let-values (((x) (values 1)) ((y) (values 2))) (list x y)) ...
  7310. (lists->string
  7311. form
  7312. `(with-let
  7313. (apply sublet (curlet)
  7314. (list ,@(map (lambda (v)
  7315. `((lambda ,(car v)
  7316. (values ,@(map (lambda (name)
  7317. (values (symbol->keyword name) name))
  7318. (args->proper-list (car v)))))
  7319. ,(cadr v)))
  7320. (cadr form))))
  7321. ,@(cddr form))))))))
  7322. (hash-special 'let-values sp-let-values))
  7323. ;; ---------------- let*-values ----------------
  7324. (hash-special 'let*-values
  7325. (lambda (caller head form env)
  7326. (if (and (pair? (cdr form))
  7327. (pair? (cadr form)))
  7328. (lint-format "perhaps ~A" caller
  7329. (lists->string form ; (let*-values (((a) (f x))) (+ a b)) -> (let ((a (f x))) (+ a b))
  7330. (let loop ((var-data (cadr form)))
  7331. (let ((v (car var-data)))
  7332. (if (and (pair? (car v)) ; just one var
  7333. (null? (cdar v)))
  7334. (if (null? (cdr var-data))
  7335. `(let ((,(caar v) ,(cadr v))) ,@(cddr form))
  7336. `(let ((,(caar v) ,(cadr v))) ,(loop (cdr var-data))))
  7337. (if (null? (cdr var-data))
  7338. `((lambda ,(car v) ,@(cddr form)) ,(cadr v))
  7339. `((lambda ,(car v) ,(loop (cdr var-data))) ,(cadr v)))))))))))
  7340. ;; ---------------- define-values ----------------
  7341. (hash-special 'define-values
  7342. (lambda (caller head form env)
  7343. (when (pair? (cdr form))
  7344. (if (null? (cadr form))
  7345. (lint-format "~A is pointless" caller (truncated-list->string form))
  7346. (when (pair? (cddr form))
  7347. (lint-format "perhaps ~A" caller ; (define-values (x y) (values 3 2)) -> (varlet (curlet) ((lambda (x y) (curlet)) (values 3 2)))
  7348. (cond ((symbol? (cadr form))
  7349. (lists->string form `(define ,(cadr form) (list ,(caddr form)))))
  7350. ((and (pair? (cadr form))
  7351. (null? (cdadr form)))
  7352. (lists->string form `(define ,(caadr form) ,(caddr form))))
  7353. (else
  7354. (let-temporarily ((target-line-length 120))
  7355. (truncated-lists->string form
  7356. `(varlet (curlet)
  7357. ((lambda ,(cadr form)
  7358. (curlet))
  7359. ,(caddr form)))))))))))))
  7360. ;; ---------------- eval ----------------
  7361. (let ()
  7362. (define (sp-eval caller head form env)
  7363. (case (length form)
  7364. ((2)
  7365. (let ((arg (cadr form)))
  7366. (if (not (pair? arg))
  7367. (if (not (symbol? arg)) ; (eval 32)
  7368. (lint-format "this eval is pointless; perhaps ~A" caller (lists->string form arg)))
  7369. (case (car arg)
  7370. ((quote) ; (eval 'x)
  7371. (lint-format "perhaps ~A" caller (lists->string form (cadr arg))))
  7372. ((string->symbol) ; (eval (string->symbol "x")) -> x
  7373. (if (string? (cadr arg))
  7374. (lint-format "perhaps ~A" caller (lists->string form (string->symbol (cadr arg))))))
  7375. ((with-input-from-string call-with-input-string)
  7376. (if (and (pair? (cdr arg)) ; (eval (call-with-input-string port read)) -> (eval-string port)
  7377. (pair? (cddr arg))
  7378. (eq? (caddr arg) 'read))
  7379. (lint-format "perhaps ~A" caller (lists->string form `(eval-string ,(cadr arg))))))
  7380. ((read)
  7381. (if (and (= (length arg) 2) ; (eval (read (open-input-string expr))) -> (eval-string expr)
  7382. (pair? (cadr arg))
  7383. (eq? (caadr arg) 'open-input-string))
  7384. (lint-format "perhaps ~A" caller (lists->string form `(eval-string ,(cadadr arg))))))
  7385. ((list)
  7386. (if (every? (lambda (p) ; (eval (list '* 2 x)) -> (* 2 (eval x))
  7387. (or (symbol? p)
  7388. (code-constant? p)))
  7389. (cdr arg))
  7390. (lint-format "perhaps ~A" caller
  7391. (lists->string form
  7392. (map (lambda (p)
  7393. (if (and (pair? p)
  7394. (eq? (car p) 'quote))
  7395. (cadr p)
  7396. (if (code-constant? p)
  7397. p
  7398. (list 'eval p))))
  7399. (cdr arg))))))))))
  7400. ((3)
  7401. (let ((arg (cadr form))
  7402. (e (caddr form)))
  7403. (if (and (pair? arg)
  7404. (eq? (car arg) 'quote))
  7405. (lint-format "perhaps ~A" caller ; (eval 'x env) -> (env 'x)
  7406. (lists->string form
  7407. (if (symbol? (cadr arg))
  7408. `(,e ,arg)
  7409. `(with-let ,e ,@(unbegin (cadr arg)))))))))))
  7410. (hash-special 'eval sp-eval))
  7411. ;; ---------------- fill! etc ----------------
  7412. (let ()
  7413. (define (sp-fill! caller head form env)
  7414. (if (= (length form) 5)
  7415. (check-start-and-end caller head (cdddr form) form env)))
  7416. (for-each (lambda (f)
  7417. (hash-special f sp-fill!))
  7418. '(fill! string-fill! list-fill! vector-fill!)))
  7419. ;; ---------------- write-string ----------------
  7420. (let ()
  7421. (define (sp-write-string caller head form env)
  7422. (cond ((= (length form) 4)
  7423. (check-start-and-end caller 'write-string (cddr form) form env))
  7424. ((and (pair? (cdr form))
  7425. (pair? (cddr form))
  7426. (pair? (caddr form))
  7427. (eq? (caaddr form) 'current-output-port))
  7428. (lint-format "(current-output-port) is the default port for ~A: ~A" caller head form))
  7429. ((equal? (cadr form) (string #\newline))
  7430. (lint-format "perhaps ~A" caller (lists->string form `(newline ,@(cddr form)))))))
  7431. (hash-special 'write-string sp-write-string))
  7432. ;; ---------------- read-line ----------------
  7433. (let ()
  7434. (define (sp-read-line caller head form env)
  7435. (if (and (= (length form) 3)
  7436. (code-constant? (caddr form))
  7437. (not (boolean? (caddr form)))) ; (read-line in-port 'concat)
  7438. (lint-format "the third argument should be boolean (#f=default, #t=include trailing newline): ~A" caller form)
  7439. (if (and (pair? (cdr form))
  7440. (pair? (cadr form))
  7441. (eq? (caadr form) 'current-input-port))
  7442. (lint-format "(current-input-port) is the default port for ~A: ~A" caller head form))))
  7443. (hash-special 'read-line sp-read-line))
  7444. ;; ---------------- string-length ----------------
  7445. (let ()
  7446. (define (sp-string-length caller head form env)
  7447. (when (= (length form) 2)
  7448. (if (string? (cadr form)) ; (string-length "asdf") -> 4
  7449. (lint-format "perhaps ~A -> ~A" caller (truncated-list->string form) (string-length (cadr form)))
  7450. (if (and (pair? (cadr form)) ; (string-length (make-string 3)) -> 3
  7451. (eq? (caadr form) 'make-string))
  7452. (lint-format "perhaps ~A" caller (lists->string form (cadadr form)))))))
  7453. (hash-special 'string-length sp-string-length))
  7454. ;; ---------------- vector-length ----------------
  7455. (let ()
  7456. (define (sp-vector-length caller head form env)
  7457. (when (= (length form) 2)
  7458. (if (vector? (cadr form))
  7459. (lint-format "perhaps ~A -> ~A" caller (truncated-list->string form) (vector-length (cadr form)))
  7460. (let ((arg (cadr form)))
  7461. (if (pair? arg)
  7462. (if (eq? (car arg) 'make-vector) ; (vector-length (make-vector 10)) -> 10
  7463. (lint-format "perhaps ~A" caller (lists->string form (cadr arg)))
  7464. (if (memq (car arg) '(copy vector-copy))
  7465. (lint-format "perhaps ~A" caller
  7466. (lists->string form ; (vector-length (vector-copy arr start end)) -> (- end start)
  7467. (if (null? (cddr arg))
  7468. `(vector-length ,(cadr arg))
  7469. (if (eq? (car arg) 'copy)
  7470. `(vector-length ,(caddr arg))
  7471. (let ((start (caddr arg))
  7472. (end (if (null? (cdddr arg))
  7473. `(vector-length ,(cadr arg))
  7474. (cadddr arg))))
  7475. `(- ,end ,start)))))))))))))
  7476. (hash-special 'vector-length sp-vector-length))
  7477. ;; ---------------- dynamic-wind ----------------
  7478. (let ()
  7479. (define (sp-dw caller head form env)
  7480. (when (= (length form) 4)
  7481. (let ((init (cadr form))
  7482. (body (caddr form))
  7483. (end (cadddr form))
  7484. (empty 0))
  7485. ;; (equal? init end) as a mistake doesn't seem to happen
  7486. (when (and (pair? init)
  7487. (eq? (car init) 'lambda))
  7488. (if (not (null? (cadr init)))
  7489. (lint-format "dynamic-wind init function should be a thunk: ~A" caller init))
  7490. (if (pair? (cddr init))
  7491. (let ((last-expr (list-ref init (- (length init) 1))))
  7492. (if (not (pair? last-expr))
  7493. (if (null? (cdddr init))
  7494. (set! empty 1))
  7495. (unless (side-effect? last-expr env)
  7496. (if (null? (cdddr init))
  7497. (set! empty 1)) ; (dynamic-wind (lambda () (s7-version)) (lambda () (list)) (lambda () #f))
  7498. (lint-format "this could be omitted: ~A in ~A" caller last-expr init))))))
  7499. (if (and (pair? body)
  7500. (eq? (car body) 'lambda))
  7501. (if (not (null? (cadr body)))
  7502. (lint-format "dynamic-wind body function should be a thunk: ~A" caller body))
  7503. (set! empty 3)) ; don't try to access body below
  7504. (when (and (pair? end)
  7505. (eq? (car end) 'lambda))
  7506. (if (not (null? (cadr end)))
  7507. (lint-format "dynamic-wind end function should be a thunk: ~A" caller end))
  7508. (if (pair? (cddr end))
  7509. (let ((last-expr (list-ref end (- (length end) 1))))
  7510. (if (not (pair? last-expr))
  7511. (if (null? (cdddr end))
  7512. (set! empty (+ empty 1)))
  7513. (unless (side-effect? last-expr env) ; or if no side-effects in any (also in init)
  7514. (if (null? (cdddr end))
  7515. (set! empty (+ empty 1)))
  7516. (lint-format "this could be omitted: ~A in ~A" caller last-expr end)))
  7517. (if (= empty 2) ; (dynamic-wind (lambda () #f) (lambda () #()) (lambda () #f)) -> #()
  7518. (lint-format "this dynamic-wind is pointless, ~A" caller
  7519. (lists->string form (if (null? (cdddr body)) (caddr body) `(begin ,@(cddr body))))))))))))
  7520. (hash-special 'dynamic-wind sp-dw))
  7521. ;; ---------------- *s7* ----------------
  7522. (hash-special '*s7*
  7523. (let ((s7-fields (let ((h (make-hash-table)))
  7524. (for-each (lambda (f)
  7525. (hash-table-set! h f #t))
  7526. '(print-length safety cpu-time heap-size free-heap-size gc-freed max-string-length max-list-length
  7527. max-vector-length max-vector-dimensions default-hash-table-length initial-string-port-length
  7528. gc-protected-objects file-names rootlet-size c-types stack-top stack-size stacktrace-defaults
  7529. max-stack-size stack catches exits float-format-precision bignum-precision default-rationalize-error
  7530. default-random-state morally-equal-float-epsilon hash-table-float-epsilon undefined-identifier-warnings
  7531. gc-stats symbol-table-locked? c-objects history-size profile-info))
  7532. h)))
  7533. (lambda (caller head form env)
  7534. (if (= (length form) 2)
  7535. (let ((arg (cadr form)))
  7536. (if (and (pair? arg)
  7537. (eq? (car arg) 'quote)
  7538. (symbol? (cadr arg)) ; (*s7* 'vector-print-length)
  7539. (not (hash-table-ref s7-fields (cadr arg))))
  7540. (lint-format "unknown *s7* field: ~A" caller arg)))))))
  7541. ;; ---------------- throw ----------------
  7542. (hash-special 'throw
  7543. (lambda (caller head form env)
  7544. (if (pair? (cdr form))
  7545. (let* ((tag (cadr form))
  7546. (eq (eqf tag env)))
  7547. (if (not (member eq '((eq? eq?) (#t #t))))
  7548. (lint-format "~A tag ~S is unreliable (catch uses eq? to match tags)" caller 'throw tag))))))
  7549. ;; ---------------- make-hash-table ----------------
  7550. (hash-special 'make-hash-table
  7551. (lambda (caller head form env)
  7552. (if (= (length form) 3)
  7553. (let ((func (caddr form)))
  7554. (if (and (symbol? func) ; (make-hash-table eq? symbol-hash)
  7555. (not (memq func '(eq? eqv? equal? morally-equal? char=? char-ci=? string=? string-ci=? =))))
  7556. (lint-format "make-hash-table function, ~A, is not a hash function" caller func))))))
  7557. ;; ---------------- deprecated funcs ----------------
  7558. (let ((deprecated-ops '((global-environment . rootlet)
  7559. (current-environment . curlet)
  7560. (make-procedure-with-setter . dilambda)
  7561. (procedure-with-setter? . dilambda?)
  7562. (make-random-state . random-state))))
  7563. (define (sp-deprecate caller head form env) ; (make-random-state 123 432)
  7564. (lint-format "~A is deprecated; use ~A" caller head (cond ((assq head deprecated-ops) => cdr))))
  7565. (for-each (lambda (op)
  7566. (hash-special (car op) sp-deprecate))
  7567. deprecated-ops))
  7568. ;; ---------------- eq null eqv equal ----------------
  7569. (let ()
  7570. (define (sp-null caller head form env)
  7571. (if (not (var-member head env)) ; (if (null (cdr x)) 0)
  7572. (lint-format "misspelled '~A? in ~A?" caller head form)))
  7573. (for-each (lambda (f)
  7574. (hash-special f sp-null))
  7575. '(null eq eqv equal))) ; (null (cdr...))
  7576. ;; ---------------- set-car set-cdr list-set vector-set string-set ----------------
  7577. (let ()
  7578. (define (sp-set caller head form env)
  7579. (if (not (var-member head env)) ; (list-set x 1 y)
  7580. (lint-format "misspelled '~A! in ~A?" caller head form)))
  7581. (for-each (lambda (f)
  7582. (hash-special f sp-set))
  7583. '(set-car set-cdr list-set vector-set string-set)))
  7584. ;; set and sort occur a million times, but aren't interesting
  7585. ;; ---------------- string-index ----------------
  7586. (let ()
  7587. (define (sp-string-index caller head form env)
  7588. (if (and (pair? (cdr form))
  7589. (pair? (cddr form))
  7590. (not (var-member 'string-index env))
  7591. (or (char? (caddr form))
  7592. (let ((sig (arg-signature (caddr form) env)))
  7593. (and (pair? sig)
  7594. (eq? (car sig) 'char?)))))
  7595. (lint-format "perhaps ~A" caller ; (string-index path #\/) -> (char-position #\/ path)
  7596. (lists->string form `(char-position ,(caddr form) ,(cadr form) ,@(cdddr form))))))
  7597. (hash-special 'string-index sp-string-index))
  7598. ;; ---------------- cons* ----------------
  7599. (let ()
  7600. (define (sp-cons* caller head form env)
  7601. (unless (var-member 'cons env)
  7602. (case (length form)
  7603. ((2) (lint-format "perhaps ~A" caller (lists->string form (cadr form))))
  7604. ((3) (lint-format "perhaps ~A" caller
  7605. (lists->string form ; cons* x y) -> (cons x y)
  7606. (if (any-null? (caddr form))
  7607. `(list ,(cadr form))
  7608. `(cons ,@(cdr form))))))
  7609. ((4) (lint-format "perhaps ~A" caller
  7610. (lists->string form ; (cons* (symbol->string v) " | " (w)) -> (cons (symbol->string v) (cons " | " (w)))
  7611. (if (any-null? (cadddr form))
  7612. `(list ,(cadr form) ,(caddr form))
  7613. `(cons ,(cadr form) (cons ,@(cddr form))))))))))
  7614. (hash-special 'cons* sp-cons*))
  7615. ;; ---------------- the-environment etc ----------------
  7616. (let ((other-names '((the-environment . curlet)
  7617. (interaction-environment . curlet)
  7618. (system-global-environment . rootlet)
  7619. (user-global-environment . rootlet)
  7620. (user-initial-environment . rootlet)
  7621. (procedure-environment . funclet)
  7622. (environment? . let?)
  7623. (environment-set! . let-set!)
  7624. (environment-ref . let-ref)
  7625. (fluid-let . let-temporarily)
  7626. (unquote-splicing apply values ...)
  7627. (bitwise-and . logand)
  7628. (bitwise-ior . logior)
  7629. (bitwise-xor . logxor)
  7630. (bitwise-not . lognot)
  7631. (bit-and . logand)
  7632. (bit-or . logior)
  7633. (bit-xor . logxor)
  7634. (bit-not . lognot)
  7635. (arithmetic-shift . ash)
  7636. (vector-for-each . for-each)
  7637. (string-for-each . for-each)
  7638. (list-copy . copy)
  7639. (bytevector? . byte-vector?)
  7640. (bytevector . byte-vector)
  7641. (make-bytevector . make-byte-vector)
  7642. (bytevector-u8-ref . byte-vector-ref)
  7643. (bytevector-u8-set! . byte-vector-set!)
  7644. (bytevector-length . length)
  7645. (write-bytevector . write-string)
  7646. (hash-set! . hash-table-set!) ; Guile
  7647. (hash-ref . hash-table-ref)
  7648. (hashq-set! . hash-table-set!)
  7649. (hashq-ref . hash-table-ref)
  7650. (hashv-set! . hash-table-set!)
  7651. (hashv-ref . hash-table-ref)
  7652. (hash-table-get . hash-table-ref) ; Gauche
  7653. (hash-table-put! . hash-table-set!)
  7654. (hash-table-num-entries . hash-table-entries)
  7655. (hashtable? . hash-table?) ; Bigloo
  7656. (hashtable-size . hash-table-entries)
  7657. (hashtable-get . hash-table-ref)
  7658. (hashtable-set! . hash-table-set!)
  7659. (hashtable-put! . hash-table-set!)
  7660. (hash-for-each . for-each)
  7661. (exact-integer? . integer?)
  7662. (truncate-quotient . quotient)
  7663. (truncate-remainder . remainder)
  7664. (floor-remainder . modulo)
  7665. (read-u8 . read-byte)
  7666. (write-u8 . write-byte)
  7667. (write-simple . write)
  7668. (peek-u8 . peek-char)
  7669. (u8-ready? . char-ready?)
  7670. (open-input-bytevector . open-input-string)
  7671. (open-output-bytevector . open-output-string)
  7672. (raise . error)
  7673. (raise-continuable . error))))
  7674. (define (sp-other-names caller head form env)
  7675. (if (not (var-member head env))
  7676. (let ((counts (or (hash-table-ref other-names-counts head) 0)))
  7677. (when (< counts 2)
  7678. (hash-table-set! other-names-counts head (+ counts 1))
  7679. (lint-format "~A is probably ~A in s7" caller head (cdr (assq head other-names)))))))
  7680. (for-each (lambda (f)
  7681. (hash-special (car f) sp-other-names))
  7682. other-names))
  7683. (hash-special '1+
  7684. (lambda (caller head form env)
  7685. (if (not (var-member '1+ env))
  7686. (lint-format "perhaps ~A" caller (lists->string form `(+ ,(cadr form) 1))))))
  7687. (let ()
  7688. (define (sp-1- caller head form env)
  7689. (if (not (var-member '-1+ env))
  7690. (lint-format "perhaps ~A" caller (lists->string form `(- ,(cadr form) 1)))))
  7691. (hash-special '-1+ sp-1-)
  7692. (hash-special '1- sp-1-))
  7693. ;; ---------------- push! pop! ----------------
  7694. (hash-special 'push!
  7695. (lambda (caller head form env) ; not predefined
  7696. (if (= (length form) 3)
  7697. (set-set (caddr form) caller form env))))
  7698. (hash-special 'pop!
  7699. (lambda (caller head form env) ; also not predefined
  7700. (if (= (length form) 2)
  7701. (set-set (cadr form) caller form env))))
  7702. ;; ---------------- receive ----------------
  7703. (hash-special 'receive
  7704. (lambda (caller head form env) ; this definition comes from Guile
  7705. (if (and (> (length form) 3)
  7706. (not (var-member 'receive env)))
  7707. ((hash-table-ref special-case-table 'call-with-values)
  7708. caller 'call-with-values
  7709. `(call-with-values
  7710. (lambda () ,(caddr form))
  7711. (lambda ,(cadr form) ,@(cdddr form)))
  7712. env))))
  7713. ;; ---------------- and=> ----------------
  7714. (hash-special 'and=>
  7715. (lambda (caller head form env) ; (and=> (ref w k) v) -> (cond ((ref w k) => v) (else #f))
  7716. (when (and (= (length form) 3)
  7717. (not (var-member 'and=> env)))
  7718. (lint-format "perhaps ~A" caller (lists->string form `(cond (,(cadr form) => ,(caddr form)) (else #f)))))))
  7719. ;; ---------------- and-let* ----------------
  7720. (let ()
  7721. (define (sp-and-let caller head form env)
  7722. (when (and (> (length form) 2)
  7723. (not (var-member 'and-let* env)))
  7724. (let loop ((bindings (cadr form)))
  7725. (cond ((pair? bindings)
  7726. (if (binding-ok? caller 'and-let* (car bindings) env #f)
  7727. (loop (cdr bindings))))
  7728. ((not (null? bindings))
  7729. (lint-format "~A variable list is not a proper list? ~S" caller 'and-let* bindings))
  7730. ((and (pair? (cadr form)) ; (and-let* ((x (f y))) (abs x)) -> (cond ((f y) => abs))
  7731. (null? (cdadr form))
  7732. (pair? (cddr form)))
  7733. (lint-format "perhaps ~A" caller
  7734. (lists->string form ; (and-let* ((x (f y))) (abs x)) -> (cond ((f y) => abs))
  7735. (if (and (null? (cdddr form))
  7736. (pair? (caddr form))
  7737. (pair? (cdaddr form))
  7738. (null? (cddr (caddr form)))
  7739. (eq? (caaadr form) (cadr (caddr form))))
  7740. `(cond (,(cadar (cadr form)) => ,(caaddr form)))
  7741. `(cond (,(cadar (cadr form)) => (lambda (,(caaadr form)) ,@(cddr form))))))))))))
  7742. (hash-special 'and-let* sp-and-let))
  7743. special-case-table))
  7744. ;; end special-case-functions
  7745. ;; ----------------------------------------
  7746. (define (unused-parameter? x) #t)
  7747. (define (unused-set-parameter? x) #t)
  7748. (define (check-args caller head form checkers env max-arity)
  7749. ;; check for obvious argument type problems
  7750. ;; caller = overall caller, head = current caller, checkers = proc or list of procs for checking args
  7751. (define (every-compatible? type1 type2)
  7752. (if (symbol? type1)
  7753. (if (symbol? type2)
  7754. (compatible? type1 type2)
  7755. (and (pair? type2) ; here everything has to match
  7756. (compatible? type1 (car type2))
  7757. (every-compatible? type1 (cdr type2))))
  7758. (and (pair? type1) ; here any match is good
  7759. (or (compatible? (car type1) type2)
  7760. (any-compatible? (cdr type1) type2)))))
  7761. (define (check-checker checker at-end)
  7762. (if (eq? checker 'integer:real?)
  7763. (if at-end 'real? 'integer?)
  7764. (if (eq? checker 'integer:any?)
  7765. (or at-end 'integer?)
  7766. checker)))
  7767. (define (any-checker? types arg)
  7768. (if (and (symbol? types)
  7769. (not (eq? types 'values)))
  7770. ((symbol->value types *e*) arg)
  7771. (and (pair? types)
  7772. (or (any-checker? (car types) arg)
  7773. (any-checker? (cdr types) arg)))))
  7774. (define (report-arg-trouble caller form head arg-number checker arg uop)
  7775. (define (prettify-arg-number argn)
  7776. (if (or (not (= argn 1))
  7777. (pair? (cddr form)))
  7778. (format #f "~D " argn)
  7779. ""))
  7780. (when (and (or arg (not (eq? checker 'output-port?)))
  7781. (not (and (eq? checker 'string?)
  7782. (pair? arg)
  7783. (eq? (car arg) 'format)
  7784. (not (null? (cadr arg))))) ; other case involves a symbol that is an output-port
  7785. (not (and (pair? arg)
  7786. (eq? (car arg) 'length)))) ; same for length
  7787. (let ((op (if (and (eq? checker 'real?)
  7788. (eq? uop 'number?))
  7789. 'complex?
  7790. uop)))
  7791. (if (and (pair? op)
  7792. (member checker op any-compatible?))
  7793. (if (and *report-sloppy-assoc*
  7794. (not (var-member :catch env)))
  7795. (lint-format* caller ; (round (char-position #\a "asb"))
  7796. (string-append "in " (truncated-list->string form) ", ")
  7797. (string-append (symbol->string head) "'s argument " (prettify-arg-number arg-number))
  7798. (string-append "should be " (prettify-checker-unq checker) ", ")
  7799. (string-append "but " (truncated-list->string arg) " might also be "
  7800. (object->string (car (remove-if (lambda (o) (any-compatible? checker o)) op))))))
  7801. (lint-format* caller ; (string-ref (char-position #\a "asb") 1)
  7802. (string-append "in " (truncated-list->string form) ", ")
  7803. (string-append (symbol->string head) "'s argument " (prettify-arg-number arg-number))
  7804. (string-append "should be " (prettify-checker-unq checker) ", ")
  7805. (string-append "but " (truncated-list->string arg) " is " (prettify-checker op)))))))
  7806. (when *report-func-as-arg-arity-mismatch*
  7807. (let ((v (var-member head env)))
  7808. (when (and (var? v)
  7809. (memq (var-ftype v) '(define define* lambda lambda*))
  7810. (zero? (var-set v)) ; perhaps this needs to wait for report-usage?
  7811. (pair? (var-arglist v)))
  7812. (let ((source (var-initial-value v)))
  7813. (when (and (pair? source)
  7814. (pair? (cdr source))
  7815. (pair? (cddr source)))
  7816. (let ((vhead (cddr source))
  7817. (head-arglist (var-arglist v))
  7818. (arg-number 1))
  7819. (when (pair? vhead)
  7820. (for-each
  7821. (lambda (arg)
  7822. ;; only check func if head is var-member and has procedure-source (var-[initial-]value?)
  7823. ;; and arg has known arity, and check only if arg(par) is car, not (for example) cadr of apply
  7824. (let ((ari (if (symbol? arg)
  7825. (arg-arity arg env)
  7826. (and (pair? arg)
  7827. (eq? (car arg) 'lambda)
  7828. (let ((len (length (cadr arg))))
  7829. (and (integer? len)
  7830. (cons (abs len)
  7831. (if (negative? len) 500000 len)))))))
  7832. (par (and (> (length head-arglist) (- arg-number 1))
  7833. (list-ref head-arglist (- arg-number 1)))))
  7834. (when (and (symbol? par)
  7835. (pair? ari)
  7836. (or (> (car ari) 0)
  7837. (< (cdr ari) 20)))
  7838. ;; fwalk below needs to be smart about tree walking so that
  7839. ;; it does not confuse (c) in (lambda (c)...) with a call on the function c.
  7840. ;; check only if current parameter name is not shadowed
  7841. (let fwalk ((sym par) (tree vhead))
  7842. (when (pair? tree)
  7843. (if (eq? (car tree) sym)
  7844. (let ((args (- (length tree) 1)))
  7845. (if (> (car ari) args)
  7846. (lint-format "~A's parameter ~A is passed ~A and called ~A, but ~A needs ~A argument~P" caller
  7847. head par
  7848. (truncated-list->string arg)
  7849. (truncated-list->string tree)
  7850. (truncated-list->string arg)
  7851. (car ari) (car ari))
  7852. (if (> args (cdr ari))
  7853. (lint-format "~A's parameter ~A is passed ~A and called ~A, but ~A takes only ~A argument~P" caller
  7854. head par
  7855. (truncated-list->string arg)
  7856. (truncated-list->string tree)
  7857. (truncated-list->string arg)
  7858. (cdr ari) (cdr ari)))))
  7859. (case (car tree)
  7860. ((let let*)
  7861. (if (and (pair? (cdr tree))
  7862. (pair? (cddr tree)))
  7863. (let ((vs ((if (symbol? (cadr tree)) caddr cadr) tree)))
  7864. (if (not (any? (lambda (a) (or (not (pair? a)) (eq? sym (car a)))) vs))
  7865. (fwalk sym ((if (symbol? (cadr tree)) cdddr cddr) tree))))))
  7866. ((do letrec letrec*)
  7867. (if (and (pair? (cdr tree))
  7868. (pair? (cddr tree))
  7869. (not (any? (lambda (a) (or (not (pair? a)) (eq? sym (car a)))) (cadr tree))))
  7870. (fwalk sym (cddr tree))))
  7871. ((lambda lambda*)
  7872. (if (and (pair? (cdr tree))
  7873. (pair? (cddr tree))
  7874. (not (any? (lambda (a) (eq? sym a)) (args->proper-list (cadr tree)))))
  7875. (fwalk sym (cddr tree))))
  7876. ((define define-constant)
  7877. (if (and (not (eq? sym (cadr tree)))
  7878. (pair? (cadr tree))
  7879. (not (any? (lambda (a) (eq? sym a)) (args->proper-list (cdadr tree)))))
  7880. (fwalk sym (cddr tree))))
  7881. ((define* define-macro define-macro* define-expansion define-bacro define-bacro*)
  7882. (if (and (pair? (cdr tree))
  7883. (pair? (cddr tree))
  7884. (not (any? (lambda (a) (eq? sym a)) (args->proper-list (cdadr tree)))))
  7885. (fwalk sym (cddr tree))))
  7886. ((quote) #f)
  7887. ((case)
  7888. (if (and (pair? (cdr tree))
  7889. (pair? (cddr tree)))
  7890. (for-each (lambda (c) (fwalk sym (cdr c))) (cddr tree))))
  7891. (else
  7892. (if (pair? (car tree))
  7893. (fwalk sym (car tree)))
  7894. (if (pair? (cdr tree))
  7895. (for-each (lambda (p) (fwalk sym p)) (cdr tree))))))))))
  7896. (set! arg-number (+ arg-number 1)))
  7897. (cdr form)))))))))
  7898. (when (pair? checkers)
  7899. (let ((arg-number 1)
  7900. (flen (- (length form) 1)))
  7901. (call-with-exit
  7902. (lambda (done)
  7903. (for-each
  7904. (lambda (arg)
  7905. (let ((checker (check-checker (if (pair? checkers) (car checkers) checkers) (= arg-number flen))))
  7906. ;; check-checker only fixes up :at-end cases
  7907. (define (check-arg expr)
  7908. (unless (symbol? expr)
  7909. (let ((op (->lint-type expr)))
  7910. (if (not (or (memq op '(#f #t values))
  7911. (every-compatible? checker op)))
  7912. (report-arg-trouble caller form head arg-number checker expr op)))))
  7913. (define (check-cond-arg expr)
  7914. (unless (symbol? expr)
  7915. (let ((op (->lint-type expr)))
  7916. (when (pair? op)
  7917. (set! op (remove 'boolean? op)) ; this is for cond test, no result -- returns test if not #f, so it can't be #f!
  7918. (if (null? (cdr op))
  7919. (set! op (car op))))
  7920. (if (not (or (memq op '(#f #t values))
  7921. (every-compatible? checker op)))
  7922. (report-arg-trouble caller form head arg-number checker expr op)))))
  7923. ;; special case checker?
  7924. (if (and (symbol? checker)
  7925. (not (memq checker '(unused-parameter? unused-set-parameter?)))
  7926. (not (hash-table-ref built-in-functions checker)))
  7927. (let ((chk (symbol->value checker)))
  7928. (if (and (procedure? chk)
  7929. (equal? (arity chk) '(2 . 2)))
  7930. (catch #t
  7931. (lambda ()
  7932. (let ((res (chk form arg-number)))
  7933. (set! checker #t)
  7934. (if (symbol? res)
  7935. (set! checker res)
  7936. (if (string? res)
  7937. (lint-format "~A's argument, ~A, should be ~A" caller head arg res)))))
  7938. (lambda (type info)
  7939. (set! checker #t))))))
  7940. (if (and (pair? arg)
  7941. (pair? (car arg)))
  7942. (let ((rtn (return-type (caar arg) env)))
  7943. (if (memq rtn '(boolean? real? integer? rational? number? complex? float? keyword? symbol? null? char?))
  7944. (lint-format* caller ; (cons ((pair? x) 2) y)
  7945. (string-append (symbol->string head) "'s argument ")
  7946. (string-append (truncated-list->string arg) " looks odd: ")
  7947. (string-append (object->string (caar arg)) " returns " (symbol->string rtn))
  7948. " which is not applicable"))))
  7949. (when (or (pair? checker)
  7950. (symbol? checker)) ; otherwise ignore type check on this argument (#t -> anything goes)
  7951. (if arg
  7952. (if (eq? checker 'unused-parameter?)
  7953. (lint-format* caller ; (define (f5 a . b) a) (f5 1 2)
  7954. (string-append (symbol->string head) "'s parameter " (number->string arg-number))
  7955. " is not used, but a value is passed: "
  7956. (truncated-list->string arg))
  7957. (if (eq? checker 'unused-set-parameter?)
  7958. (lint-format* caller ; (define (f21 x y) (set! x 3) (+ y 1)) (f21 (+ z 1) z)
  7959. (string-append (symbol->string head) "'s parameter " (number->string arg-number))
  7960. "'s value is not used, but a value is passed: "
  7961. (truncated-list->string arg)))))
  7962. (if (not (pair? arg))
  7963. (let ((val (cond ((not (symbol? arg))
  7964. arg)
  7965. ((constant? arg)
  7966. (symbol->value arg))
  7967. ((and (hash-table-ref built-in-functions arg)
  7968. (not (var-member :with-let env))
  7969. (not (var-member arg env)))
  7970. (symbol->value arg *e*))
  7971. (else arg))))
  7972. (if (not (or (and (symbol? val)
  7973. (not (keyword? val)))
  7974. (any-checker? checker val)))
  7975. (let ((op (->lint-type val)))
  7976. (unless (memq op '(#f #t values))
  7977. (report-arg-trouble caller form head arg-number checker arg op)))))
  7978. (case (car arg)
  7979. ((quote) ; '1 -> 1
  7980. (let ((op (if (pair? (cadr arg)) 'list?
  7981. (if (symbol? (cadr arg))
  7982. 'symbol?
  7983. (->lint-type (cadr arg))))))
  7984. ;; arg is quoted expression
  7985. (if (not (or (memq op '(#f #t values))
  7986. (every-compatible? checker op)))
  7987. (report-arg-trouble caller form head arg-number checker arg op))))
  7988. ;; arg is an expression
  7989. ((begin let let* letrec letrec* with-let)
  7990. (check-arg (and (pair? (cdr arg))
  7991. (list-ref arg (- (length arg) 1)))))
  7992. ((if)
  7993. (if (and (pair? (cdr arg))
  7994. (pair? (cddr arg)))
  7995. (let ((t (caddr arg))
  7996. (f (if (pair? (cdddr arg)) (cadddr arg))))
  7997. (check-arg t)
  7998. (when (and f (not (symbol? f)))
  7999. (check-arg f)))))
  8000. ((dynamic-wind catch)
  8001. (if (= (length arg) 4)
  8002. (let ((f (caddr arg)))
  8003. (if (and (pair? f)
  8004. (eq? (car f) 'lambda))
  8005. (let ((len (length f)))
  8006. (if (> len 2)
  8007. (check-arg (list-ref f (- len 1)))))))))
  8008. ((do)
  8009. (if (and (pair? (cdr arg))
  8010. (pair? (cddr arg)))
  8011. (let ((end+res (caddr arg)))
  8012. (check-arg (if (pair? (cdr end+res))
  8013. (list-ref end+res (- (length end+res) 1))
  8014. ())))))
  8015. ((case)
  8016. (for-each
  8017. (lambda (clause)
  8018. (if (and (pair? clause)
  8019. (pair? (cdr clause))
  8020. (not (eq? (cadr clause) '=>)))
  8021. (check-arg (list-ref clause (- (length clause) 1)))))
  8022. (cddr arg)))
  8023. ((cond)
  8024. (for-each
  8025. (lambda (clause)
  8026. (if (pair? clause)
  8027. (if (pair? (cdr clause))
  8028. (if (not (eq? (cadr clause) '=>))
  8029. (check-arg (list-ref clause (- (length clause) 1))))
  8030. (check-cond-arg (car clause)))))
  8031. (cdr arg)))
  8032. ((call/cc call-with-exit call-with-current-continuation)
  8033. ;; find func in body (as car of list), check its arg as return value
  8034. (when (and (pair? (cdr arg))
  8035. (pair? (cadr arg))
  8036. (eq? (caadr arg) 'lambda))
  8037. (let ((f (cdadr arg)))
  8038. (when (and (pair? f)
  8039. (pair? (car f))
  8040. (symbol? (caar f))
  8041. (null? (cdar f)))
  8042. (define c-walk
  8043. (let ((rtn (caar f)))
  8044. (lambda (tree)
  8045. (if (pair? tree)
  8046. (if (eq? (car tree) rtn)
  8047. (check-arg (if (null? (cdr tree)) () (cadr tree)))
  8048. (begin
  8049. (c-walk (car tree))
  8050. (for-each (lambda (x) (if (pair? x) (c-walk x))) (cdr tree))))))))
  8051. (for-each c-walk (cdr f))))))
  8052. ((values)
  8053. (cond ((not (positive? (length arg))))
  8054. ((null? (cdr arg)) ; #<unspecified>
  8055. (if (not (any-checker? checker #<unspecified>))
  8056. (report-arg-trouble caller form head arg-number checker arg 'unspecified?)))
  8057. ((null? (cddr arg))
  8058. (check-arg (cadr arg)))
  8059. (else
  8060. (for-each
  8061. (lambda (expr rest)
  8062. (check-arg expr)
  8063. (set! arg-number (+ arg-number 1))
  8064. (if (> arg-number max-arity) (done))
  8065. (if (list? checkers)
  8066. (if (null? (cdr checkers))
  8067. (done)
  8068. (set! checkers (cdr checkers)))))
  8069. (cdr arg) (cddr arg))
  8070. (check-arg (list-ref arg (- (length arg) 1))))))
  8071. (else
  8072. (let ((op (return-type (car arg) env)))
  8073. (let ((v (var-member (car arg) env)))
  8074. (if (and (var? v)
  8075. (not (memq form (var-history v))))
  8076. (set! (var-history v) (cons form (var-history v)))))
  8077. ;; checker is arg-type, op is expression type (can also be a pair)
  8078. (if (and (not (memq op '(#f #t values)))
  8079. (not (memq checker '(unused-parameter? unused-set-parameter?)))
  8080. (or (not (every-compatible? checker op))
  8081. (and (just-constants? arg env) ; try to eval the arg
  8082. (catch #t
  8083. (lambda ()
  8084. (not (any-checker? checker (eval arg))))
  8085. (lambda ignore-catch-error-args
  8086. #f)))))
  8087. (report-arg-trouble caller form head arg-number checker arg op)))))))
  8088. (if (list? checkers)
  8089. (if (null? (cdr checkers))
  8090. (done)
  8091. (set! checkers (cdr checkers)))
  8092. (if (memq checker '(unused-parameter? unused-set-parameter?))
  8093. (set! checker #t)))
  8094. (set! arg-number (+ arg-number 1))
  8095. (if (> arg-number max-arity) (done))))
  8096. (cdr form)))))))
  8097. (define check-unordered-exprs
  8098. (let ((changers (let ((h (make-hash-table)))
  8099. (for-each (lambda (s)
  8100. (hash-table-set! h s #t))
  8101. '(set!
  8102. read read-byte read-char read-line read-string
  8103. write write-byte write-char write-string format display newline
  8104. reverse! set-cdr! sort! string-fill! vector-fill! fill!
  8105. emergency-exit exit error throw))
  8106. h)))
  8107. (lambda (caller form vals env)
  8108. (define (report-trouble)
  8109. (lint-format* caller ; (let ((x (read-byte)) (y (read-byte))) (- x y))
  8110. (string-append "order of evaluation of " (object->string (car form)) "'s ")
  8111. (string-append (if (memq (car form) '(let letrec do)) "bindings" "arguments") " is unspecified, ")
  8112. (string-append "so " (truncated-list->string form) " is trouble")))
  8113. (let ((reads ())
  8114. (writes ())
  8115. (jumps ()))
  8116. (call-with-exit
  8117. (lambda (return)
  8118. (for-each (lambda (p)
  8119. (when (and (pair? p)
  8120. (not (var-member (car p) env))
  8121. (hash-table-ref changers (car p)))
  8122. (if (pair? jumps)
  8123. (return (report-trouble)))
  8124. (case (car p)
  8125. ((read read-char read-line read-byte)
  8126. (if (null? (cdr p))
  8127. (if (memq () reads)
  8128. (return (report-trouble))
  8129. (set! reads (cons () reads)))
  8130. (if (memq (cadr p) reads)
  8131. (return (report-trouble))
  8132. (set! reads (cons (cadr p) reads)))))
  8133. ((read-string)
  8134. (if (or (null? (cdr p))
  8135. (null? (cddr p)))
  8136. (if (memq () reads)
  8137. (return (report-trouble))
  8138. (set! reads (cons () reads)))
  8139. (if (memq (caddr p) reads)
  8140. (return (report-trouble))
  8141. (set! reads (cons (caddr p) reads)))))
  8142. ((display write write-char write-string write-byte)
  8143. (if (null? (cddr p))
  8144. (if (memq () writes)
  8145. (return (report-trouble))
  8146. (set! writes (cons () writes)))
  8147. (if (memq (caddr p) writes)
  8148. (return (report-trouble))
  8149. (set! writes (cons (caddr p) writes)))))
  8150. ((newline)
  8151. (if (null? (cdr p))
  8152. (if (memq () writes)
  8153. (return (report-trouble))
  8154. (set! writes (cons () writes)))
  8155. (if (memq (cadr p) writes)
  8156. (return (report-trouble))
  8157. (set! writes (cons (cadr p) writes)))))
  8158. ((format)
  8159. (if (and (pair? (cdr p))
  8160. (not (string? (cadr p)))
  8161. (cadr p)) ; i.e. not #f
  8162. (if (memq (cadr p) writes)
  8163. (return (report-trouble))
  8164. (set! writes (cons (cadr p) writes)))))
  8165. ((fill! string-fill! vector-fill! reverse! sort! set! set-cdr!)
  8166. ;; here there's trouble if cadr used anywhere -- but we need to check for shadowing
  8167. (if (any? (lambda (np)
  8168. (and (not (eq? np p))
  8169. (tree-memq (cadr p) np)))
  8170. vals)
  8171. (return (report-trouble))))
  8172. ((throw error exit emergency-exit)
  8173. (if (or (pair? reads) ; jumps already checked above
  8174. (pair? writes))
  8175. (return (report-trouble))
  8176. (set! jumps (cons p jumps)))))))
  8177. vals)))))))
  8178. (define check-call
  8179. (let ((repeated-args-table (let ((h (make-hash-table)))
  8180. (for-each
  8181. (lambda (op)
  8182. (set! (h op) #t))
  8183. '(= / max min < > <= >= - quotient remainder modulo rationalize and or
  8184. string=? string<=? string>=? string<? string>? string-ci=? string-ci<=? string-ci>=? string-ci<? string-ci>?
  8185. char=? char<=? char>=? char<? char>? char-ci=? char-ci<=? char-ci>=? char-ci<? char-ci>?
  8186. boolean=? symbol=?))
  8187. h))
  8188. (repeated-args-table-2 (let ((h (make-hash-table)))
  8189. (for-each
  8190. (lambda (op)
  8191. (set! (h op) #t))
  8192. '(= max min < > <= >= and or
  8193. string=? string<=? string>=? string<? string>? string-ci=? string-ci<=? string-ci>=? string-ci<? string-ci>?
  8194. char=? char<=? char>=? char<? char>? char-ci=? char-ci<=? char-ci>=? char-ci<? char-ci>?
  8195. boolean=? symbol=?))
  8196. h)))
  8197. (lambda (caller head form env)
  8198. (let ((data (var-member head env)))
  8199. (if (and (pair? (cdr form))
  8200. (pair? (cddr form))
  8201. (any-procedure? head env))
  8202. (check-unordered-exprs caller form (cdr form) env))
  8203. (if (var? data)
  8204. (let ((fdata (cdr data)))
  8205. ;; a local var
  8206. (when (symbol? (fdata 'ftype))
  8207. (let ((args (fdata 'arglist))
  8208. (ary (and (not (eq? (fdata 'decl) 'error))
  8209. (arity (fdata 'decl))))
  8210. (sig (var-signature data)))
  8211. (when (pair? ary)
  8212. (let ((req (car ary))
  8213. (opt (cdr ary))
  8214. (pargs (if (pair? args)
  8215. (proper-list args)
  8216. (if (symbol? args)
  8217. (list args)
  8218. ()))))
  8219. (let ((call-args (- (length form) 1)))
  8220. (if (< call-args req)
  8221. (begin
  8222. (for-each (lambda (p)
  8223. (if (pair? p)
  8224. (let ((v (var-member (car p) env)))
  8225. (if (var? v)
  8226. (let ((vals (let-ref (cdr v) 'values)))
  8227. (if (pair? vals)
  8228. (set! call-args (+ call-args -1 (cadr vals)))))))))
  8229. (cdr form))
  8230. (if (not (or (>= call-args req)
  8231. (tree-memq 'values (cdr form))
  8232. (tree-memq 'dilambda (fdata 'initial-value))))
  8233. (lint-format "~A needs ~D argument~A: ~A"
  8234. caller head
  8235. req (if (> req 1) "s" "")
  8236. (truncated-list->string form))))
  8237. (if (> (- call-args (keywords (cdr form))) opt) ; multiple-values can make this worse, (values)=nothing doesn't apply here
  8238. (lint-format "~A has too many arguments: ~A" caller head (truncated-list->string form)))))
  8239. (unless (fdata 'allow-other-keys)
  8240. (let ((last-was-key #f)
  8241. (have-keys 0)
  8242. (warned #f)
  8243. (rest (if (and (pair? form) (pair? (cdr form))) (cddr form) ())))
  8244. (for-each
  8245. (lambda (arg)
  8246. (if (and (keyword? arg)
  8247. (not last-was-key)) ; keyarg might have key value
  8248. (begin
  8249. (set! have-keys (+ have-keys 1))
  8250. (if (not (member (keyword->symbol arg) pargs
  8251. (lambda (a b)
  8252. (eq? a (if (pair? b) (car b) b)))))
  8253. (lint-format "~A keyword argument ~A (in ~A) does not match any argument in ~S" caller
  8254. head arg (truncated-list->string form) pargs))
  8255. (if (memq arg rest)
  8256. (lint-format "~W is repeated in ~A" caller arg (cdr form)))
  8257. (set! last-was-key #t))
  8258. (begin
  8259. (when (and (positive? have-keys)
  8260. (not last-was-key)
  8261. (not warned))
  8262. (set! warned #t)
  8263. (lint-format "non-keyword argument ~A follows previous keyword~P" caller arg have-keys))
  8264. (set! last-was-key #f)))
  8265. (if (pair? rest)
  8266. (set! rest (cdr rest))))
  8267. (cdr form))))
  8268. (check-args caller head form (if (pair? sig) (cdr sig) ()) env opt)
  8269. ;; for a complete var-history, we could run through the args here even if no type info
  8270. ;; also if var passed to macro -- what to do?
  8271. ;; look for problematic macro expansion
  8272. (when (memq (fdata 'ftype) '(define-macro define-macro* defmacro defmacro*))
  8273. (unless (list? (fdata 'macro-ops))
  8274. (let ((syms (list () ())))
  8275. (tree-symbol-walk ((if (memq (fdata 'ftype) '(define-macro define-macro*))
  8276. cddr cdddr)
  8277. (fdata 'initial-value))
  8278. syms)
  8279. (varlet fdata 'macro-locals (car syms) 'macro-ops (cadr syms))))
  8280. (when (or (pair? (fdata 'macro-locals))
  8281. (pair? (fdata 'macro-ops)))
  8282. (let ((bad-locals ())
  8283. (bad-quoted-locals ()))
  8284. (for-each
  8285. (lambda (local)
  8286. (if (tree-unquoted-member local (cdr form))
  8287. (set! bad-locals (cons local bad-locals))))
  8288. (fdata 'macro-locals))
  8289. (when (null? bad-locals)
  8290. (for-each
  8291. (lambda (local)
  8292. (if (tree-member local (cdr form))
  8293. (set! bad-quoted-locals (cons local bad-quoted-locals))))
  8294. (fdata 'macro-locals)))
  8295. (let ((bad-ops ()))
  8296. (for-each
  8297. (lambda (op)
  8298. (let ((curf (var-member op env))
  8299. (oldf (var-member op (fdata 'env))))
  8300. (if (and (not (eq? curf oldf))
  8301. (or (pair? (fdata 'env))
  8302. (defined? op (rootlet))))
  8303. (set! bad-ops (cons op bad-ops)))))
  8304. (fdata 'macro-ops))
  8305. (when (or (pair? bad-locals)
  8306. (pair? bad-quoted-locals)
  8307. ;; (define-macro (mac8 b) `(let ((a 12)) (+ (symbol->value ,b) a)))
  8308. ;; (let ((a 1)) (mac8 'a))
  8309. ;; far-fetched!
  8310. (pair? bad-ops))
  8311. (lint-format "possible problematic macro expansion:~% ~A ~A collide with subsequently defined ~A~A~A"
  8312. caller
  8313. (truncated-list->string form)
  8314. (if (or (pair? bad-locals)
  8315. (pair? bad-ops))
  8316. "may"
  8317. "could conceivably")
  8318. (if (pair? bad-locals)
  8319. (format #f "~{'~A~^, ~}" bad-locals)
  8320. (if (pair? bad-quoted-locals)
  8321. (format #f "~{'~A~^, ~}" bad-quoted-locals)
  8322. ""))
  8323. (if (and (pair? bad-locals) (pair? bad-ops)) ", " "")
  8324. (if (pair? bad-ops)
  8325. (format #f "~{~A~^, ~}" bad-ops)
  8326. "")))))))
  8327. )))))
  8328. ;; not local var
  8329. (when (symbol? head)
  8330. (let ((head-value (symbol->value head *e*))) ; head might be "arity"!
  8331. (when (or (procedure? head-value)
  8332. (macro? head-value))
  8333. ;; check arg number
  8334. (let ((ary (arity head-value)))
  8335. (let ((args (- (length form) 1))
  8336. (min-arity (car ary))
  8337. (max-arity (cdr ary)))
  8338. (if (< args min-arity)
  8339. (lint-format "~A needs ~A~D argument~A: ~A"
  8340. caller head
  8341. (if (= min-arity max-arity) "" "at least ")
  8342. min-arity
  8343. (if (> min-arity 1) "s" "")
  8344. (truncated-list->string form))
  8345. (if (and (not (procedure-setter head-value))
  8346. (> (- args (keywords (cdr form))) max-arity))
  8347. (lint-format "~A has too many arguments: ~A" caller head (truncated-list->string form))))
  8348. (when (and (procedure? head-value)
  8349. (pair? (cdr form))) ; there are args (the not-enough-args case is checked above)
  8350. (if (zero? max-arity)
  8351. (lint-format "too many arguments: ~A" caller (truncated-list->string form))
  8352. (begin
  8353. (for-each (lambda (arg)
  8354. (if (pair? arg)
  8355. (if (negative? (length arg))
  8356. (lint-format "missing quote? ~A in ~A" caller arg form)
  8357. (if (eq? (car arg) 'unquote)
  8358. (lint-format "stray comma? ~A in ~A" caller arg form)))))
  8359. (cdr form))
  8360. ;; if keywords, check that they are acceptable
  8361. ;; this only applies to lambda*'s that have been previously loaded (lint doesn't create them)
  8362. (let ((source (procedure-source head-value)))
  8363. (if (and (pair? source)
  8364. (eq? (car source) 'lambda*))
  8365. (let ((decls (cadr source)))
  8366. (if (not (memq :allow-other-keys decls))
  8367. (for-each
  8368. (lambda (arg)
  8369. (if (and (keyword? arg)
  8370. (not (eq? arg :rest))
  8371. (not (member arg decls
  8372. (lambda (a b)
  8373. (eq? (keyword->symbol a) (if (pair? b) (car b) b))))))
  8374. (lint-format "~A keyword argument ~A (in ~A) does not match any argument in ~S" caller
  8375. head arg (truncated-list->string form) decls)))
  8376. (cdr form))))))
  8377. ;; we've already checked for head in the current env above
  8378. (if (and (or (memq head '(eq? eqv?))
  8379. (and (= (length form) 3)
  8380. (hash-table-ref repeated-args-table head)))
  8381. (repeated-member? (cdr form) env))
  8382. (lint-format "this looks odd: ~A"
  8383. caller
  8384. ;; sigh (= a a) could be used to check for non-finite numbers, I suppose,
  8385. ;; and (/ 0 0) might be deliberate (as in gmp)
  8386. ;; also (min (random x) (random x)) is not pointless
  8387. (truncated-list->string form))
  8388. (if (and (hash-table-ref repeated-args-table-2 head)
  8389. (repeated-member? (cdr form) env))
  8390. (lint-format "it looks odd to have repeated arguments in ~A" caller (truncated-list->string form))))
  8391. (when (memq head '(eq? eqv?))
  8392. (define (repeated-member-with-not? lst env)
  8393. (and (pair? lst)
  8394. (let ((this-repeats (and (not (and (pair? (car lst))
  8395. (side-effect? (car lst) env)))
  8396. (or (member (list 'not (car lst)) (cdr lst))
  8397. (and (pair? (car lst))
  8398. (eq? (caar lst) 'not)
  8399. (= (length (car lst)) 2)
  8400. (member (cadar lst) (cdr lst)))))))
  8401. (or this-repeats
  8402. (repeated-member-with-not? (cdr lst) env)))))
  8403. (if (repeated-member-with-not? (cdr form) env)
  8404. (lint-format "this looks odd: ~A" caller (truncated-list->string form))))
  8405. ;; now try to check arg types
  8406. (let ((arg-data (cond ((procedure-signature (symbol->value head *e*)) => cdr) (else #f))))
  8407. (if (pair? arg-data)
  8408. (check-args caller head form arg-data env max-arity))
  8409. ))))))))))))))
  8410. (define (indirect-set? vname func arg1)
  8411. (case func
  8412. ((set-car! set-cdr! vector-set! list-set! string-set!)
  8413. (eq? arg1 vname))
  8414. ((set!)
  8415. (and (pair? arg1)
  8416. (eq? (car arg1) vname)))
  8417. (else #f)))
  8418. (define (env-difference name e1 e2 lst)
  8419. (if (or (null? e1)
  8420. (null? e2)
  8421. (eq? (car e1) (car e2)))
  8422. (reverse lst)
  8423. (env-difference name (cdr e1) e2
  8424. (if (eq? name (var-name (car e1)))
  8425. lst
  8426. (cons (car e1) lst)))))
  8427. (define report-usage
  8428. (let ((unwrap-cxr (hash-table '(caar car) '(cadr cdr) '(cddr cdr) '(cdar car)
  8429. '(caaar caar car) '(caadr cadr cdr) '(caddr cddr cdr) '(cdddr cddr cdr)
  8430. '(cdaar caar car) '(cddar cdar car) '(cadar cadr car) '(cdadr cadr cdr)
  8431. '(cadddr cdddr cddr cdr) '(cddddr cdddr cddr cdr) '(caaaar caaar caar car) '(caaadr caadr cadr cdr)
  8432. '(caadar cadar cdar car) '(caaddr caddr cddr cdr) '(cadaar cdaar caar car) '(cadadr cdadr cadr cdr)
  8433. '(caddar cddar cdar car) '(cdaaar caaar caar car) '(cdaadr caadr cadr cdr) '(cdadar cadar cdar car)
  8434. '(cdaddr caddr cddr cdr) '(cddaar cdaar caar car) '(cddadr cdadr cadr cdr) '(cdddar cddar cdar car))))
  8435. (lambda (caller head vars env)
  8436. ;; report unused or set-but-unreferenced variables, then look at the overall history
  8437. ;; vars used before defined are kind of a mess -- history has #f for the (unknown) enclosing form
  8438. ;; and any definition wipes out the accumulated pre-def uses -- this should be by closed-body and
  8439. ;; ignore local defines (i.e. really only define[x] propagates backwards) -- changing this is
  8440. ;; tricky (fools current unused func arg + value message for example).
  8441. (define (all-types-agree v)
  8442. (let ((base-type (->lint-type (var-initial-value v)))
  8443. (vname (var-name v)))
  8444. (let ((typef (lambda (p)
  8445. (or (not (and (pair? p)
  8446. (eq? (car p) 'set!)
  8447. (eq? vname (cadr p))))
  8448. (let ((nt (->lint-type (caddr p))))
  8449. (or (subsumes? base-type nt)
  8450. (and (subsumes? nt base-type)
  8451. (set! base-type nt))
  8452. (and (memq nt '(pair? null? proper-list?))
  8453. (memq base-type '(pair? null? proper-list?))
  8454. (set! base-type 'list?))))))))
  8455. (and (every? typef (var-history v))
  8456. base-type))))
  8457. (when (and (not (eq? head 'begin)) ; begin can redefine = set a variable
  8458. (pair? vars)
  8459. (proper-list? vars))
  8460. (do ((cur vars (cdr cur))
  8461. (rst (cdr vars) (cdr rst)))
  8462. ((null? rst))
  8463. (let ((vn (var-name (car cur))))
  8464. (if (not (memq vn '(:lambda :dilambda)))
  8465. (let ((repeat (var-member vn rst)))
  8466. (when repeat
  8467. (let ((type (if (eq? (var-definer repeat) 'parameter) 'parameter 'variable)))
  8468. (if (eq? (var-definer (car cur)) 'define)
  8469. (lint-format "~A ~A ~A is redefined ~A" caller head type vn
  8470. (if (equal? head "")
  8471. (if (not (tree-memq vn (var-initial-value (car cur))))
  8472. "at the top level."
  8473. (format #f "at the top level. Perhaps use set! instead: ~A"
  8474. (truncated-list->string `(set! ,vn ,(var-initial-value (car cur))))))
  8475. (format #f "in the ~A body. Perhaps use set! instead: ~A"
  8476. head (truncated-list->string `(set! ,vn ,(var-initial-value (car cur)))))))
  8477. (lint-format "~A ~A ~A is declared twice" caller head type vn)))))))))
  8478. (let ((old-line-number line-number)
  8479. (outer-form (cond ((var-member :let env) => var-initial-value) (else #f))))
  8480. (for-each
  8481. (lambda (local-var)
  8482. (let ((vname (var-name local-var))
  8483. (otype (if (eq? (var-definer local-var) 'parameter) 'parameter 'variable)))
  8484. ;; (let ((x 0)...) ... (set! x 1)...) -> move the set! value to let init value
  8485. ;; car body as set! is handled in let-walker etc
  8486. (when (and (pair? outer-form)
  8487. (positive? (var-set local-var))
  8488. (memq (car outer-form) '(let let*))
  8489. (list? (cadr outer-form))
  8490. (not (side-effect? (var-initial-value local-var) env)))
  8491. (let ((nxt (let ((len (length (var-history local-var))))
  8492. (and (> len 1)
  8493. (list-ref (var-history local-var) (- len 2))))))
  8494. (when (and (pair? nxt)
  8495. (eq? (car nxt) 'set!)
  8496. (eq? (cadr nxt) vname)
  8497. (code-constant? (caddr nxt)) ; so vname is not involved etc
  8498. (not (tree-memq vname (caddr outer-form))) ; not redundant with next -- need to exclude this case
  8499. (let ((f (member vname (cdddr outer-form) tree-memq)))
  8500. (and (pair? f)
  8501. (eq? (car f) nxt))))
  8502. (lint-format "perhaps change ~A's initial value to ~A, and remove ~A in ~A" caller
  8503. vname (caddr nxt) nxt (truncated-list->string outer-form)))))
  8504. ;; if's possible for an unused function to have ref=1, null cdr history, but it appears to
  8505. ;; always involve curlet exports and the like.
  8506. ;; do all refs to an unset var go through the same function (at some level)
  8507. (when (and (zero? (var-set local-var))
  8508. (> (var-ref local-var) 1))
  8509. (let ((hist (var-history local-var)))
  8510. (when (and (pair? hist)
  8511. (pair? outer-form) ; if outer-form is #f, local-var is probably a top-level var
  8512. (not (and (memq (car outer-form) '(let let*)) ; not a named-let parameter
  8513. (symbol? (cadr outer-form)))))
  8514. (let ((first (car hist))) ; all but the initial binding have to match this
  8515. (when (pair? first)
  8516. (let ((op (car first)))
  8517. (when (and (symbol? op)
  8518. (not (eq? op 'unquote))
  8519. (not (hash-table-ref makers op))
  8520. (not (eq? vname op)) ; not a function (this kind if repetition is handled elsewhere)
  8521. (pair? (cdr hist))
  8522. (pair? (cddr hist))
  8523. (pair? (cdr first))
  8524. (not (side-effect? first env))
  8525. (every? (lambda (a)
  8526. (or (eq? a vname)
  8527. (code-constant? a)))
  8528. (cdr first))
  8529. (or (code-constant? (var-initial-value local-var))
  8530. (= (tree-count1 vname first 0) 1))
  8531. (every? (lambda (a)
  8532. (and (pair? a)
  8533. (or (equal? first a)
  8534. (and (eq? (hash-table-ref reversibles (car first)) (car a))
  8535. (equal? (cdr first) (reverse (cdr a))))
  8536. (set! op (match-cxr op (car a))))))
  8537. (if (eq? otype 'parameter)
  8538. (cdr hist)
  8539. (copy (cdr hist) (make-list (- (length hist) 2))))))
  8540. (let* ((new-op (or op (car first)))
  8541. (set-target (let walker ((tree outer-form)) ; check for new-op dilambda as target of set!
  8542. (and (pair? tree)
  8543. (or (and (eq? (car tree) 'set!)
  8544. (pair? (cdr tree))
  8545. (pair? (cadr tree))
  8546. (eq? (caadr tree) new-op))
  8547. (walker (car tree))
  8548. (walker (cdr tree)))))))
  8549. (unless set-target
  8550. (if (eq? otype 'parameter)
  8551. (if (> (var-ref local-var) 2)
  8552. (lint-format "parameter ~A is always accessed (~A times) via ~S" caller
  8553. vname (var-ref local-var) `(,new-op ,@(cdr first))))
  8554. (lint-format* caller
  8555. (symbol->string vname)
  8556. " is not set, and is always accessed via "
  8557. (object->string `(,new-op ,@(cdr first)))
  8558. " so its binding could probably be "
  8559. ;; "probably" here because the accesses could have hidden protective assumptions
  8560. ;; i.e. full accessor is not valid at point of let binding
  8561. (object->string `(,vname (,new-op ,@(tree-subst (var-initial-value local-var) vname (cdr first)))))
  8562. " in "
  8563. (truncated-list->string outer-form))))))))))))
  8564. ;; translate to dilambda fixing arg if necessary and mention generic set!
  8565. (let ((init (var-initial-value local-var)))
  8566. (when (and (pair? init)
  8567. (eq? (car init) 'define)
  8568. (pair? (cadr init)))
  8569. (let* ((vstr (symbol->string vname))
  8570. (len (length vstr)))
  8571. (when (> len 4)
  8572. (let ((setv #f)
  8573. (newv #f))
  8574. (if (string=? (substring vstr 0 4) "get-")
  8575. (let ((sv (symbol "set-" (substring vstr 4))))
  8576. (set! setv (or (var-member sv vars)
  8577. (var-member sv env)))
  8578. (set! newv (string->symbol (substring vstr 4))))
  8579. (if (string=? (substring vstr (- len 4)) "-ref")
  8580. (let ((sv (symbol (substring vstr 0 (- len 4)) "-set!")))
  8581. (set! setv (or (var-member sv vars)
  8582. (var-member sv env)))
  8583. (set! newv (string->symbol (substring vstr 0 (- len 4)))))
  8584. (let ((pos (string-position "-get-" vstr)))
  8585. (when pos ; this doesn't happen very often, others: Get-, -ref-, -set!- are very rare
  8586. (let ((sv (let ((s (copy vstr))) (set! (s (+ pos 1)) #\s) (string->symbol s))))
  8587. (set! setv (or (var-member sv vars)
  8588. (var-member sv env)))
  8589. (set! newv (symbol (substring vstr 0 pos)
  8590. (substring vstr (+ pos 4))))))))) ; +4 to include #\-
  8591. (when (and setv
  8592. (not (var-member newv vars))
  8593. (not (var-member newv env)))
  8594. (let ((getter init)
  8595. (setter (var-initial-value setv)))
  8596. (when (and (pair? setter)
  8597. (eq? (car setter) 'define)
  8598. (pair? (cadr setter)))
  8599. (let ((getargs (cdadr getter))
  8600. (setargs (cdadr setter)))
  8601. (unless (null? setargs)
  8602. (if (or (eq? newv getargs)
  8603. (and (pair? getargs)
  8604. (memq newv getargs)))
  8605. (let ((unique (find-unique-name getter newv)))
  8606. (set! getter (tree-subst unique newv getter))
  8607. (set! getargs (cdadr getter))))
  8608. (if (or (eq? newv setargs)
  8609. (and (pair? setargs)
  8610. (memq newv setargs)))
  8611. (let ((unique (find-unique-name setter newv)))
  8612. (set! setter (tree-subst unique newv setter))
  8613. (set! setargs (cdadr setter))))
  8614. (let ((getdots (if (null? getargs) "" " ..."))
  8615. (setdots (if (or (not (pair? setargs)) (null? (cdr setargs))) "" " ..."))
  8616. (setvalue (and (proper-list? setargs)
  8617. (list-ref setargs (- (length setargs) 1)))))
  8618. (if setvalue
  8619. (format outport "~NC~A: perhaps use dilambda and generalized set! for ~A and ~A:~%~
  8620. ~NCreplace (~A~A) with (~A~A) and (~A~A ~A) with (set! (~A~A) ~A)~%~
  8621. ~NC~A~%"
  8622. lint-left-margin #\space
  8623. caller
  8624. vname (var-name setv)
  8625. (+ lint-left-margin 4) #\space
  8626. vname getdots newv getdots
  8627. (var-name setv) setdots setvalue
  8628. newv setdots setvalue
  8629. (+ lint-left-margin 4) #\space
  8630. (lint-pp `(define ,newv (dilambda
  8631. (lambda ,getargs ,@(cddr getter))
  8632. (lambda ,setargs ,@(cddr setter))))))))))))))))))
  8633. ;; bad variable names
  8634. (cond ((hash-table-ref syntaces vname)
  8635. (lint-format "~A ~A named ~A is asking for trouble" caller head otype vname))
  8636. ((eq? vname 'l)
  8637. (lint-format "\"l\" is a really bad variable name" caller))
  8638. ((and *report-built-in-functions-used-as-variables*
  8639. (hash-table-ref built-in-functions vname))
  8640. (lint-format "~A ~A named ~A is asking for trouble" caller
  8641. (if (and (pair? (var-scope local-var))
  8642. (null? (cdr (var-scope local-var)))
  8643. (symbol? (car (var-scope local-var))))
  8644. (car (var-scope local-var))
  8645. head)
  8646. otype vname))
  8647. (else (check-for-bad-variable-name caller vname)))
  8648. (unless (memq vname '(:lambda :dilambda))
  8649. (if (and (eq? otype 'variable)
  8650. (or *report-unused-top-level-functions*
  8651. (not (eq? caller top-level:))))
  8652. (let ((scope (var-scope local-var))) ; might be #<undefined>?
  8653. (if (pair? scope) (set! scope (remove vname scope)))
  8654. (when (and (pair? scope)
  8655. (null? (cdr scope))
  8656. (symbol? (car scope))
  8657. (not (var-member (car scope) (let search ((e env))
  8658. (if (null? e)
  8659. env
  8660. (if (eq? (caar e) vname)
  8661. e
  8662. (search (cdr e))))))))
  8663. (format outport "~NC~A~A is ~A only in ~A~%"
  8664. lint-left-margin #\space
  8665. (if (eq? caller top-level:)
  8666. "top-level: "
  8667. "")
  8668. vname
  8669. (if (memq (var-ftype local-var) '(define lambda define* lambda*)) "called" "used")
  8670. (car scope)))))
  8671. (if (and (eq? (var-ftype local-var) 'define-expansion)
  8672. (not (eq? caller top-level:)))
  8673. (format outport "~NCdefine-expansion for ~A is not at the top-level, so it is ignored~%"
  8674. lint-left-margin #\space
  8675. vname))
  8676. ;; look for port opened but not closed
  8677. ;; (let ((p (open-output-file str))) (display 32 p) x)
  8678. (when (and (pair? outer-form)
  8679. (not (tree-memq vname (list-ref outer-form (- (length outer-form) 1))))) ; vname never returned from outer-form??
  8680. (let ((hist (var-history local-var))
  8681. (open-set '(open-input-string open-input-file open-output-string open-output-file))
  8682. (open-form #f))
  8683. (when (and (any? (lambda (tree)
  8684. (and (pair? tree)
  8685. (or (and (memq (car tree) open-set)
  8686. (pair? (cdr tree))
  8687. (not (memq vname (cdr tree))))
  8688. (and (eq? (car tree) 'set!)
  8689. (pair? (cdr tree))
  8690. (eq? (cadr tree) vname)
  8691. (pair? (cddr tree))
  8692. (pair? (caddr tree))
  8693. (memq (caaddr tree) open-set)))
  8694. (set! open-form tree)))
  8695. hist)
  8696. (not (tree-set-member '(close-input-port close-output-port close-port close current-output-port current-input-port) hist)))
  8697. (lint-format "in ~A~% perhaps ~A is opened via ~A, but never closed" caller
  8698. (truncated-list->string outer-form)
  8699. vname open-form))))
  8700. ;; redundant vars are hard to find -- tons of false positives
  8701. (if (zero? (var-ref local-var))
  8702. (when (and (or (not (equal? head ""))
  8703. *report-unused-top-level-functions*)
  8704. (or *report-unused-parameters*
  8705. (not (eq? otype 'parameter))))
  8706. (if (positive? (var-set local-var))
  8707. (let ((sets (map (lambda (call)
  8708. (if (and (pair? call)
  8709. (not (eq? (var-definer local-var) 'do))
  8710. (eq? (car call) 'set!)
  8711. (eq? (cadr call) vname))
  8712. call
  8713. (values)))
  8714. (var-history local-var))))
  8715. (if (pair? sets)
  8716. (if (null? (cdr sets))
  8717. (lint-format "~A set, but not used: ~A" caller
  8718. vname (truncated-list->string (car sets)))
  8719. (lint-format "~A set, but not used: ~{~S~^ ~}" caller
  8720. vname sets))
  8721. (lint-format "~A set, but not used: ~A from ~A" caller
  8722. vname (truncated-list->string (var-initial-value local-var)) (var-definer local-var))))
  8723. ;; not ref'd or set
  8724. (if (not (memq vname '(documentation signature iterator? defanimal)))
  8725. (let ((val (if (pair? (var-history local-var)) (car (var-history local-var)) (var-initial-value local-var)))
  8726. (def (var-definer local-var)))
  8727. (let-temporarily ((line-number (if (eq? caller top-level:) -1 line-number)))
  8728. ;; eval confuses this message (eval '(+ x 1)), no other use of x [perhaps check :let initial-value = outer-form]
  8729. ;; so does let-ref syntax: (apply (*e* 'g1)...) will miss this reference to g1
  8730. (if (symbol? def)
  8731. (if (eq? otype 'parameter)
  8732. (lint-format "~A not used" caller vname)
  8733. (lint-format* caller
  8734. (string-append (object->string vname) " not used, initially: ")
  8735. (string-append (truncated-list->string val) " from " (symbol->string def))))
  8736. (lint-format* caller
  8737. (string-append (object->string vname) " not used, value: ")
  8738. (truncated-list->string val))))))))
  8739. ;; not zero var-ref
  8740. (let ((arg-type #f))
  8741. (when (and (not (memq (var-definer local-var) '(parameter named-let named-let*)))
  8742. (pair? (var-history local-var))
  8743. (or (zero? (var-set local-var))
  8744. (set! arg-type (all-types-agree local-var))))
  8745. (let ((vtype (or arg-type ; this can't be #f unless no sets so despite appearances there's no contention here
  8746. (eq? caller top-level:) ; might be a global var where init value is largely irrelevant
  8747. (->lint-type (var-initial-value local-var))))
  8748. (lit? (code-constant? (var-initial-value local-var))))
  8749. (do ((clause (var-history local-var) (cdr clause)))
  8750. ((null? (cdr clause))) ; ignore the initial value which depends on a different env
  8751. (let ((call (car clause)))
  8752. (if (pair? call) (set! line-number (pair-line-number call)))
  8753. (when (pair? call)
  8754. (let ((func (car call))
  8755. (call-arg1 (and (pair? (cdr call)) (cadr call))))
  8756. ;; check for assignments into constants
  8757. (if (and lit?
  8758. (indirect-set? vname func call-arg1))
  8759. (lint-format "~A's value, ~A, is a literal constant, so this set! is trouble: ~A" caller
  8760. vname (var-initial-value local-var) (truncated-list->string call)))
  8761. (when (symbol? vtype)
  8762. (when (and (not (eq? caller top-level:))
  8763. (not (memq vtype '(boolean? #t values)))
  8764. (memq func '(if when unless)) ; look for (if x ...) where x is never #f, this happens a dozen or so times
  8765. (or (eq? (cadr call) vname)
  8766. (and (pair? (cadr call))
  8767. (eq? (caadr call) 'not)
  8768. (eq? (cadadr call) vname))))
  8769. (lint-format "~A is never #f, so ~A" caller
  8770. vname
  8771. (lists->string
  8772. call
  8773. (if (eq? vname (cadr call))
  8774. (case func
  8775. ((if) (caddr call))
  8776. ((when) (if (pair? (cdddr call)) `(begin ,@(cddr call)) (caddr call)))
  8777. ((unless) #<unspecified>))
  8778. (case func
  8779. ((if) (if (pair? (cdddr call)) (cadddr call)))
  8780. ((when) #<unspecified>)
  8781. ((unless) (if (pair? (cdddr call)) `(begin ,@(cddr call)) (caddr call))))))))
  8782. ;; check for incorrect types in function calls
  8783. (unless (memq vtype '(boolean? null?)) ; null? here avoids problems with macros that call set!
  8784. (let ((p (memq vname (cdr call))))
  8785. (when (pair? p)
  8786. (let ((sig (arg-signature func env))
  8787. (pos (- (length call) (length p))))
  8788. (when (and (pair? sig)
  8789. (< pos (length sig)))
  8790. (let ((desired-type (list-ref sig pos)))
  8791. (if (not (compatible? vtype desired-type))
  8792. (lint-format "~A is ~A, but ~A in ~A wants ~A" caller
  8793. vname (prettify-checker-unq vtype)
  8794. func (truncated-list->string call)
  8795. (prettify-checker desired-type))))))))
  8796. (let ((suggest made-suggestion))
  8797. ;; check for pointless vtype checks
  8798. (when (and (hash-table-ref bools func)
  8799. (not (eq? vname func)))
  8800. (when (or (eq? vtype func)
  8801. (and (compatible? vtype func)
  8802. (not (subsumes? vtype func))))
  8803. (lint-format "~A is ~A, so ~A is #t" caller vname (prettify-checker-unq vtype) call))
  8804. (unless (compatible? vtype func)
  8805. (lint-format "~A is ~A, so ~A is #f" caller vname (prettify-checker-unq vtype) call)))
  8806. (case func
  8807. ;; need a way to mark exported variables so they won't be checked in this process
  8808. ;; case can happen here, but it never seems to trigger a type error
  8809. ((eq? eqv? equal?)
  8810. ;; (and (pair? x) (eq? x #\a)) etc
  8811. (when (or (and (code-constant? call-arg1)
  8812. (not (compatible? vtype (->lint-type call-arg1))))
  8813. (and (code-constant? (caddr call))
  8814. (not (compatible? vtype (->lint-type (caddr call))))))
  8815. (lint-format "~A is ~A, so ~A is #f" caller vname (prettify-checker-unq vtype) call)))
  8816. ((and or)
  8817. (when (let amidst? ((lst call))
  8818. (and (pair? lst)
  8819. (pair? (cdr lst))
  8820. (or (eq? (car lst) vname)
  8821. (amidst? (cdr lst))))) ; don't clobber possible trailing vname (returned by expression)
  8822. (lint-format "~A is ~A, so ~A" caller ; (let ((x 1)) (and x (< x 1))) -> (< x 1)
  8823. vname (prettify-checker-unq vtype)
  8824. (lists->string call
  8825. (simplify-boolean (remove vname call) () () vars)))))
  8826. ((not)
  8827. (if (eq? vname (cadr call))
  8828. (lint-format "~A is ~A, so ~A" caller
  8829. vname (prettify-checker-unq vtype)
  8830. (lists->string call #f))))
  8831. ((/) (if (and (number? (var-initial-value local-var))
  8832. (zero? (var-initial-value local-var))
  8833. (zero? (var-set local-var))
  8834. (memq vname (cddr call)))
  8835. (lint-format "~A is ~A, so ~A is an error" caller
  8836. vname (var-initial-value local-var)
  8837. call))))
  8838. ;; the usual eqx confusion
  8839. (when (and (= suggest made-suggestion)
  8840. (memq vtype '(char? number? integer? real? float? rational? complex?)))
  8841. (if (memq func '(eq? equal?))
  8842. (lint-format "~A is ~A, so ~A ~A be eqv? in ~A" caller
  8843. vname (prettify-checker-unq vtype) func
  8844. (if (eq? func 'eq?) "should" "could")
  8845. call))
  8846. ;; check other boolean exprs
  8847. (when (and (zero? (var-set local-var))
  8848. (number? (var-initial-value local-var))
  8849. (eq? vname call-arg1)
  8850. (null? (cddr call))
  8851. (hash-table-ref booleans func))
  8852. (let ((val (catch #t
  8853. (lambda ()
  8854. ((symbol->value func (rootlet)) (var-initial-value local-var)))
  8855. (lambda args
  8856. 'error))))
  8857. (if (boolean? val)
  8858. (lint-format "~A is ~A, so ~A is ~A" caller vname (var-initial-value local-var) call val))))))
  8859. ;; implicit index checks -- these are easily fooled by macros
  8860. (when (and (memq vtype '(vector? float-vector? int-vector? string? list? byte-vector?))
  8861. (pair? (cdr call)))
  8862. (when (eq? func vname)
  8863. (let ((init (var-initial-value local-var)))
  8864. (if (not (compatible? 'integer? (->lint-type call-arg1)))
  8865. (lint-format "~A is ~A, but the index ~A is ~A" caller
  8866. vname (prettify-checker-unq vtype)
  8867. call-arg1 (prettify-checker (->lint-type call-arg1))))
  8868. (if (integer? call-arg1)
  8869. (if (negative? call-arg1)
  8870. (lint-format "~A's index ~A is negative" caller vname call-arg1)
  8871. (if (zero? (var-set local-var))
  8872. (let ((lim (cond ((code-constant? init)
  8873. (length init))
  8874. ((memq (car init) '(vector float-vector int-vector string list byte-vector))
  8875. (- (length init) 1))
  8876. (else
  8877. (and (pair? (cdr init))
  8878. (integer? (cadr init))
  8879. (memq (car init) '(make-vector make-float-vector make-int-vector
  8880. make-string make-list make-byte-vector))
  8881. (cadr init))))))
  8882. (if (and (real? lim)
  8883. (>= call-arg1 lim))
  8884. (lint-format "~A has length ~A, but index is ~A" caller vname lim call-arg1))))))))
  8885. (when (eq? func 'implicit-set)
  8886. ;; ref is already checked in other history entries
  8887. (let ((ref-type (case vtype
  8888. ((float-vector?) 'real?) ; not 'float? because ints are ok here
  8889. ((int-vector? byte-vector?) 'integer)
  8890. ((string?) 'char?)
  8891. (else #f))))
  8892. (if ref-type
  8893. (let ((val-type (->lint-type (caddr call))))
  8894. (if (not (compatible? val-type ref-type))
  8895. (lint-format "~A wants ~A, but the value in ~A is ~A" caller
  8896. vname (prettify-checker-unq ref-type)
  8897. `(set! ,@(cdr call))
  8898. (prettify-checker val-type)))))
  8899. ))))))
  8900. ))) ; do loop through clauses
  8901. ;; check for duplicated calls involving local-var
  8902. (when (and (> (var-ref local-var) 8)
  8903. (zero? (var-set local-var))
  8904. (eq? (var-ftype local-var) #<undefined>))
  8905. (let ((h (make-hash-table)))
  8906. (for-each (lambda (call)
  8907. (when (and (pair? call)
  8908. (not (eq? (car call) vname)) ; ignore functions for now
  8909. (not (side-effect? call env)))
  8910. (hash-table-set! h call (+ 1 (or (hash-table-ref h call) 0)))
  8911. (cond ((hash-table-ref unwrap-cxr (car call))
  8912. => (lambda (lst)
  8913. (for-each (lambda (c)
  8914. (hash-table-set! h (cons c (cdr call)) (+ 1 (or (hash-table-ref h (cons c (cdr call))) 0))))
  8915. lst))))))
  8916. (var-history local-var))
  8917. (let ((repeats ()))
  8918. (for-each (lambda (call)
  8919. (if (and (> (cdr call) (max 3 (/ 20 (tree-leaves (car call))))) ; was 5
  8920. (not (memq (caar call) '(make-vector make-float-vector)))
  8921. (or (null? (cddar call))
  8922. (every? (lambda (p)
  8923. (or (not (symbol? p))
  8924. (eq? p vname)))
  8925. (cdar call))))
  8926. (set! repeats (cons (string-append (truncated-list->string (car call)) " occurs ")
  8927. (cons (string-append (object->string (cdr call)) " times"
  8928. (if (pair? repeats) ", " ""))
  8929. repeats)))))
  8930. h)
  8931. (if (pair? repeats)
  8932. (apply lint-format*
  8933. caller
  8934. (string-append (object->string vname) " is not set, but ")
  8935. repeats)))))
  8936. ;; check for function parameters whose values never change and are not just symbols
  8937. (when (and (> (var-ref local-var) 3)
  8938. (zero? (var-set local-var))
  8939. (memq (var-ftype local-var) '(define lambda))
  8940. (pair? (var-arglist local-var))
  8941. (let loop ((calls (var-history local-var))) ; if func passed as arg, ignore it
  8942. (or (null? calls)
  8943. (null? (cdr calls))
  8944. (and (pair? (car calls))
  8945. (not (memq (var-name local-var) (cdar calls)))
  8946. (loop (cdr calls))))))
  8947. (let ((pars (map list (proper-list (var-arglist local-var)))))
  8948. (do ((clauses (var-history local-var) (cdr clauses)))
  8949. ((null? (cdr clauses))) ; ignore the initial value
  8950. (if (and (pair? (car clauses))
  8951. (eq? (caar clauses) (var-name local-var)))
  8952. (for-each (lambda (arg par) ; collect all arguments for each parameter
  8953. (if (not (member arg (cdr par))) ; we haven't seen this argument yet, so
  8954. (set-cdr! par (cons arg (cdr par))))) ; add it to the list for this parameter
  8955. (cdar clauses)
  8956. pars)))
  8957. (for-each (lambda (p)
  8958. (if (and (pair? (cdr p))
  8959. (null? (cddr p)) ; so all calls, this parameter has the same value
  8960. (not (symbol? (cadr p))))
  8961. (lint-format "~A's '~A parameter is always ~S (~D calls)" caller
  8962. (var-name local-var) (car p) (cadr p) (var-ref local-var))))
  8963. pars)))
  8964. )))) ; end (if zero var-ref)
  8965. ;; vars with multiple incompatible ascertainable types don't happen much and obvious type errors are extremely rare
  8966. (when (and *report-clobbered-function-return-value*
  8967. (positive? (var-set local-var)))
  8968. (let ((start (var-initial-value local-var)))
  8969. (let ((func #f)
  8970. (retcons? (and (pair? start)
  8971. (let ((v (var-member (car start) env)))
  8972. (and (var? v)
  8973. (eq? (var-retcons v) #t))))))
  8974. (for-each (lambda (f)
  8975. (when (pair? f)
  8976. (case (car f)
  8977. ((set!)
  8978. (set! retcons? (and (pair? (cdr f))
  8979. (eq? (cadr f) vname)
  8980. (pair? (cddr f))
  8981. (pair? (caddr f))
  8982. (let ((v (var-member (caaddr f) env)))
  8983. (and (var? v)
  8984. (eq? #t (var-retcons v))
  8985. (set! func f))))))
  8986. ((string-set! list-set! vector-set! set-car! set-cdr!)
  8987. (if (and retcons?
  8988. (eq? (cadr f) vname))
  8989. (lint-format "~A returns a constant sequence, but ~A appears to clobber it" caller
  8990. func f))))))
  8991. (reverse (var-history local-var))))))
  8992. )))
  8993. vars)
  8994. (set! line-number old-line-number)))))
  8995. (define (find-call sym body)
  8996. (call-with-exit
  8997. (lambda (return)
  8998. (let tree-call ((tree body))
  8999. (if (and (pair? tree)
  9000. (not (eq? (car tree) 'quote)))
  9001. (begin
  9002. (if (eq? (car tree) sym)
  9003. (return tree))
  9004. (if (memq (car tree) '(let let* letrec letrec* do lambda lambda* define))
  9005. (return #f)) ; possible shadowing -- not worth the infinite effort to corroborate
  9006. (if (pair? (car tree))
  9007. (tree-call (car tree)))
  9008. (if (pair? (cdr tree))
  9009. (do ((p (cdr tree) (cdr p)))
  9010. ((not (pair? p)) #f)
  9011. (tree-call (car p))))))))))
  9012. (define (check-returns caller f env) ; f is not the last form in the body
  9013. (if (not (or (side-effect? f env)
  9014. (eq? '=> f)))
  9015. (lint-format "this could be omitted: ~A" caller (truncated-list->string f))
  9016. (when (pair? f)
  9017. (case (car f)
  9018. ((if)
  9019. (when (and (pair? (cdr f))
  9020. (pair? (cddr f)))
  9021. (let ((true (caddr f))
  9022. (false (if (pair? (cdddr f)) (cadddr f) 'no-false)))
  9023. (let ((true-ok (side-effect? true env))
  9024. (false-ok (or (eq? false 'no-false)
  9025. (side-effect? false env))))
  9026. (if true-ok
  9027. (if (pair? true)
  9028. (check-returns caller true env))
  9029. (lint-format "this branch is pointless~A: ~A in ~A" caller
  9030. (local-line-number true)
  9031. (truncated-list->string true)
  9032. (truncated-list->string f)))
  9033. (if false-ok
  9034. (if (pair? false)
  9035. (check-returns caller false env))
  9036. (lint-format "this branch is pointless~A: ~A in ~A" caller
  9037. (local-line-number false)
  9038. (truncated-list->string false)
  9039. (truncated-list->string f)))))))
  9040. ((cond case)
  9041. ;; here all but last result exprs are already checked
  9042. ;; redundant begin can confuse this, but presumably we'll complain about that elsewhere
  9043. ;; also even in mid-body, if else clause has a side-effect, an earlier otherwise pointless clause might be avoiding that
  9044. (let ((has-else (let ((last-clause (list-ref f (- (length f) 1))))
  9045. (and (pair? last-clause)
  9046. (memq (car last-clause) '(else #t))
  9047. (any? (lambda (c)
  9048. (side-effect? c env))
  9049. (cdr last-clause))))))
  9050. (for-each (lambda (c)
  9051. (if (and (pair? c)
  9052. (pair? (cdr c))
  9053. (not (memq '=> (cdr c))))
  9054. (let ((last-expr (list-ref c (- (length c) 1))))
  9055. (cond ((side-effect? last-expr env)
  9056. (if (pair? last-expr)
  9057. (check-returns caller last-expr env)))
  9058. (has-else
  9059. (if (or (pair? (cddr c))
  9060. (eq? (car f) 'cond))
  9061. (lint-format "this ~A clause's result could be omitted" caller
  9062. (truncated-list->string c))
  9063. (if (not (memq last-expr '(#f #t #<unspecified>))) ; it's not already obvious
  9064. (lint-format "this ~A clause's result could be simply #f" caller
  9065. (truncated-list->string c)))))
  9066. ((and (eq? (car f) 'case)
  9067. (or (eq? last-expr (cadr c))
  9068. (not (any? (lambda (p) (side-effect? p env)) (cdr c)))))
  9069. (lint-format "this case clause can be omitted: ~A" caller
  9070. (truncated-list->string c)))
  9071. (else (lint-format "this is pointless: ~A in ~A" caller
  9072. (truncated-list->string last-expr)
  9073. (truncated-list->string c)))))))
  9074. ((if (eq? (car f) 'cond) cdr cddr) f))))
  9075. ((let let*)
  9076. (if (and (pair? (cdr f))
  9077. (not (symbol? (cadr f)))
  9078. (pair? (cddr f)))
  9079. (let ((last-expr (list-ref f (- (length f) 1))))
  9080. (if (side-effect? last-expr env)
  9081. (if (pair? last-expr)
  9082. (check-returns caller last-expr env))
  9083. (lint-format "this is pointless~A: ~A in ~A" caller
  9084. (local-line-number last-expr)
  9085. (truncated-list->string last-expr)
  9086. (truncated-list->string f))))))
  9087. ;; perhaps use truncated-lists->string here??
  9088. ((and)
  9089. (let ((len (length f)))
  9090. (case len
  9091. ((1) (lint-format "this ~A is pointless" caller f))
  9092. ((2) (lint-format "perhaps ~A" caller (lists->string f (cadr f))))
  9093. ((3) (lint-format "perhaps ~A" caller (lists->string f `(if ,(cadr f) ,(caddr f))))) ; (begin (and x (display y)) (log z)) -> (if x (display y))
  9094. (else (lint-format "perhaps ~A" caller (lists->string f `(if ,(cadr f) (and ,@(cddr f)))))))))
  9095. ((or)
  9096. (let ((len (length f)))
  9097. (case len
  9098. ((1) (lint-format "this ~A is pointless" caller f))
  9099. ((2) (lint-format "perhaps ~A" caller (lists->string f (cadr f))))
  9100. ((3) (lint-format "perhaps ~A" caller (lists->string f `(if (not ,(cadr f)) ,(caddr f)))))
  9101. (else (lint-format "perhaps ~A" caller (lists->string f `(if (not ,(cadr f)) (or ,@(cddr f)))))))))
  9102. ((not)
  9103. (lint-format "this ~A is pointless" caller f))
  9104. ((letrec letrec* with-let unless when begin with-baffle)
  9105. (if (and (pair? (cdr f))
  9106. (pair? (cddr f)))
  9107. (let ((last-expr (list-ref f (- (length f) 1))))
  9108. (if (side-effect? last-expr env)
  9109. (if (pair? last-expr)
  9110. (check-returns caller last-expr env))
  9111. ;; (begin (if x (begin (display x) z)) z)
  9112. (lint-format "this is pointless~A: ~A in ~A" caller
  9113. (local-line-number last-expr)
  9114. (truncated-list->string last-expr)
  9115. (truncated-list->string f))))))
  9116. ((do)
  9117. (let ((returned (if (and (pair? (cdr f))
  9118. (pair? (cddr f)))
  9119. (let ((end+res (caddr f)))
  9120. (if (pair? (cdr end+res))
  9121. (list-ref end+res (- (length end+res) 1)))))))
  9122. (if (or (eq? returned #<unspecified>)
  9123. (and (pair? returned)
  9124. (side-effect? returned env)))
  9125. (if (pair? returned)
  9126. (check-returns caller returned env))
  9127. ;; (begin (do ((i 0 (+ i 1))) ((= i 10) i) (display i)) x)
  9128. (lint-format "~A: result ~A~A is not used" caller
  9129. (truncated-list->string f)
  9130. (truncated-list->string returned)
  9131. (local-line-number returned)))))
  9132. ((call-with-exit)
  9133. (if (and (pair? (cdr f))
  9134. (pair? (cadr f))
  9135. (eq? (caadr f) 'lambda)
  9136. (pair? (cdadr f))
  9137. (pair? (cadadr f)))
  9138. (let ((return (car (cadadr f))))
  9139. (let walk ((tree (cddadr f)))
  9140. (if (pair? tree)
  9141. (if (eq? (car tree) return)
  9142. (if (and (pair? (cdr tree))
  9143. (or (not (boolean? (cadr tree)))
  9144. (pair? (cddr tree))))
  9145. ;; (begin (call-with-exit (lambda (quit) (if (< x 0) (quit (+ x 1))) (display x))) (+ x 2))
  9146. (lint-format "th~A call-with-exit return value~A will be ignored: ~A" caller
  9147. (if (pair? (cddr tree))
  9148. (values "ese" "s")
  9149. (values "is" ""))
  9150. tree))
  9151. (for-each walk tree)))))))
  9152. ((map)
  9153. (if (pair? (cdr f)) ; (begin (map g123 x) x)
  9154. (lint-format "map could be for-each: ~A" caller (truncated-list->string `(for-each ,@(cdr f))))))
  9155. ((reverse!)
  9156. (if (pair? (cdr f)) ; (let ((x (list 23 1 3))) (reverse! x) x)
  9157. (lint-format "~A might leave ~A in an undefined state; perhaps ~A" caller (car f) (cadr f)
  9158. `(set! ,(cadr f) ,f))))
  9159. ((format)
  9160. (if (and (pair? (cdr f))
  9161. (eq? (cadr f) #t)) ; (let () (format #t "~A" x) x)
  9162. (lint-format "perhaps use () with format since the string value is discarded:~% ~A"
  9163. caller `(format () ,@(cddr f)))))))))
  9164. (define lint-current-form #f)
  9165. (define lint-mid-form #f)
  9166. (define (escape? form env)
  9167. (and (pair? form)
  9168. (let ((v (var-member (car form) env)))
  9169. (if (var? v)
  9170. (memq (var-definer v) '(call/cc call-with-current-continuation call-with-exit))
  9171. (memq (car form) '(error throw))))))
  9172. (define (lint-walk-body caller head body env)
  9173. (when (pair? body)
  9174. (when (and (pair? (car body))
  9175. (pair? (cdar body)))
  9176. (when (and (not (eq? last-rewritten-internal-define (car body))) ; we already rewrote this
  9177. (pair? (cdr body)) ; define->named let, but this is only ok in a "closed" situation, not (begin (define...)) for example
  9178. (pair? (cadr body))
  9179. (memq (caar body) '(define define*))
  9180. (pair? (cadar body)))
  9181. (let ((fname (caadar body))
  9182. (fargs (cdadar body))
  9183. (fbody (cddar body)))
  9184. (when (and (symbol? fname)
  9185. (proper-list? fargs)
  9186. (= (tree-count1 fname (cdr body) 0) 1)
  9187. (not (any? keyword? fargs)))
  9188. (let ((call (find-call fname (cdr body))))
  9189. (when (pair? call)
  9190. (let ((new-args (if (eq? (caar body) 'define)
  9191. (map list fargs (cdr call))
  9192. (let loop ((pars fargs)
  9193. (vals (cdr call))
  9194. (args ()))
  9195. (if (null? pars)
  9196. (reverse args)
  9197. (loop (cdr pars)
  9198. (if (pair? vals)
  9199. (values (cdr vals)
  9200. (cons (list ((if (pair? (car pars)) caar car) pars) (car vals)) args))
  9201. (values ()
  9202. (cons (if (pair? (car pars)) (car pars) (list (car pars) #f)) args))))))))
  9203. (new-let (if (eq? (caar body) 'define) 'let 'let*)))
  9204. (if (and (pair? fbody)
  9205. (pair? (cdr fbody))
  9206. (string? (car fbody)))
  9207. (set! fbody (cdr fbody)))
  9208. ;; (... (define* (f1 a b) (+ a b)) (f1 :c 1)) -> (... (let ((a :c) (b 1)) (+ a b)))
  9209. (lint-format "perhaps ~A" caller
  9210. (lists->string `(... ,@body)
  9211. (if (= (tree-count2 fname body 0) 2)
  9212. (if (null? fargs)
  9213. (if (null? (cdr fbody))
  9214. `(... ,@(tree-subst (car fbody) call (cdr body)))
  9215. `(... ,@(tree-subst `(let () ,@fbody) call (cdr body))))
  9216. `(... ,@(tree-subst `(let ,new-args ,@fbody) call (cdr body))))
  9217. `(... ,@(tree-subst `(,new-let ,fname ,new-args ,@fbody) call (cdr body))))))))))))
  9218. ;; look for non-function defines at the start of the body and use let(*) instead
  9219. ;; we're in a closed body here, so the define can't propagate backwards
  9220. (let ((first-expr (car body)))
  9221. ;; another case: f(args) (let(...)set! arg < no let>)
  9222. (when (and (eq? (car first-expr) 'define)
  9223. (symbol? (cadr first-expr))
  9224. (pair? (cddr first-expr))
  9225. ;;(not (tree-car-member (cadr first-expr) (caddr first-expr)))
  9226. ;;(not (tree-set-car-member '(lambda lambda*) (caddr first-expr)))
  9227. (not (and (pair? (caddr first-expr))
  9228. (memq (caaddr first-expr) '(lambda lambda*))))
  9229. (> (length body) 2))
  9230. ;; this still is not ideal -- we need to omit let+lambda as well
  9231. (do ((names ())
  9232. (letx 'let)
  9233. (vars&vals ())
  9234. (p body (cdr p)))
  9235. ((not (and (pair? p)
  9236. (let ((expr (car p)))
  9237. (and (pair? expr)
  9238. (eq? (car expr) 'define)
  9239. (symbol? (cadr expr)) ; not (define (f ...))
  9240. (pair? (cddr expr))
  9241. (not (and (pair? (caddr expr)) ; not (define f (lambda...))
  9242. (memq (caaddr expr) '(lambda let lambda* let* letrec letrec*))))))))
  9243. ;; (... (define x 3) 32) -> (... (let ((x 3)) ...))
  9244. (if (pair? vars&vals)
  9245. (lint-format "perhaps ~A" caller
  9246. (lists->string `(... ,@body)
  9247. `(... (,letx ,(reverse vars&vals)
  9248. ...))))))
  9249. ;; define acts like letrec(*), not let -- reference to name in lambda body is current name
  9250. (let ((expr (cdar p)))
  9251. (set! vars&vals (cons (if (< (tree-leaves (cdr expr)) 12)
  9252. expr
  9253. (list (car expr) '...))
  9254. vars&vals))
  9255. (if (tree-set-member names (cdr expr))
  9256. (set! letx 'let*))
  9257. (set! names (cons (car expr) names)))))))
  9258. (let ((len (length body)))
  9259. (when (> len 2) ; ... (define (x...)...) (x ...) -> (let (...) ...) or named let -- this happens a lot!
  9260. (let ((n-1 (list-ref body (- len 2))) ; or (define (x ...)...) (some expr calling x once) -> named let etc
  9261. (n (list-ref body (- len 1))))
  9262. (when (and (pair? n-1)
  9263. (eq? (car n-1) 'define)
  9264. (pair? (cadr n-1))
  9265. (symbol? (caadr n-1))
  9266. (proper-list? (cdadr n-1))
  9267. (pair? n)
  9268. (or (and (eq? (car n) (caadr n-1))
  9269. (eqv? (length (cdadr n-1)) (length (cdr n)))) ; not values -> let!
  9270. (and (< (tree-leaves n-1) 12)
  9271. (tree-car-member (caadr n-1) (cdr n)) ; skip car -- see preceding
  9272. (= (tree-count1 (caadr n-1) n 0) 1))))
  9273. (let ((outer-form (cond ((var-member :let env) => var-initial-value) (else #f)))
  9274. (new-var (caadr n-1)))
  9275. (when (and (pair? outer-form)
  9276. (not (let walker ((tree outer-form)) ; check even the enclosing env -- define in do body back ref'd in stepper for example
  9277. (or (eq? new-var tree)
  9278. (and (pair? tree)
  9279. (not (eq? n tree))
  9280. (not (eq? n-1 tree))
  9281. (not (eq? (car tree) 'quote))
  9282. (or (walker (car tree))
  9283. (walker (cdr tree))))))))
  9284. (let ((named (if (tree-memq new-var (cddr n-1)) (list new-var) ())))
  9285. (if (eq? (car n) (caadr n-1))
  9286. (lint-format "perhaps change ~A to a ~Alet: ~A" caller new-var (if (pair? named) "named " "")
  9287. (lists->string outer-form `(... (let ,@named ,(map list (cdadr n-1) (cdr n)) ...))))
  9288. (let ((call (find-call new-var n)))
  9289. (when (and (pair? call)
  9290. (eqv? (length (cdadr n-1)) (length (cdr call))))
  9291. (let ((new-call `(let ,@named ,(map list (cdadr n-1) (cdr call)) ,@(cddr n-1))))
  9292. (lint-format "perhaps embed ~A: ~A" caller new-var
  9293. (lists->string outer-form `(... ,(tree-subst new-call call n)))))))))))))
  9294. (let ((suggest made-suggestion))
  9295. (unless (tree-memq 'curlet (list-ref body (- len 1)))
  9296. (do ((q body (cdr q))
  9297. (k 0 (+ k 1)))
  9298. ((null? q))
  9299. (let ((expr (car q)))
  9300. (when (and (pair? expr)
  9301. (eq? (car expr) 'define)
  9302. (pair? (cdr expr))
  9303. (pair? (cddr expr))
  9304. (null? (cdddr expr)))
  9305. (let ((name (and (symbol? (cadr expr)) (cadr expr))))
  9306. (when name
  9307. (do ((last-ref k)
  9308. (p (cdr q) (cdr p))
  9309. (i (+ k 1) (+ i 1)))
  9310. ((null? p)
  9311. (if (and (< k last-ref (+ k 2))
  9312. (pair? (list-ref body (+ k 1))))
  9313. (let ((end-dots (if (< last-ref (- len 1)) '(...) ()))
  9314. (letx (if (tree-member name (cddr expr)) 'letrec 'let))
  9315. (use-expr (list-ref body (+ k 1)))
  9316. (seen-earlier (or (var-member name env)
  9317. (do ((s body (cdr s)))
  9318. ((or (eq? s q)
  9319. (and (pair? (car s))
  9320. (tree-memq name (car s))))
  9321. (not (eq? s q)))))))
  9322. (cond (seen-earlier)
  9323. ((not (eq? (car use-expr) 'define))
  9324. (let-temporarily ((target-line-length 120))
  9325. ;; (... (define f14 (lambda (x y) (if (positive? x) (+ x y) y))) (+ (f11 1 2) (f14 1 2))) ->
  9326. ;; (... (let ((f14 (lambda (x y) (if (positive? x) (+ x y) y)))) (+ (f11 1 2) (f14 1 2))))
  9327. (lint-format "the scope of ~A could be reduced: ~A" caller name
  9328. (truncated-lists->string `(... ,expr ,use-expr ,@end-dots)
  9329. `(... (,letx ((,name ,(caddr expr)))
  9330. ,use-expr)
  9331. ,@end-dots)))))
  9332. ((eq? (cadr use-expr) name)
  9333. ;; (let () (display 33) (define x 2) (define x (+ x y)) (display 43)) ->
  9334. ;; (... (set! x (+ x y)) ...)
  9335. (lint-format "use set! to redefine ~A: ~A" caller name
  9336. (lists->string `(... ,use-expr ,@end-dots)
  9337. `(... (set! ,name ,(caddr use-expr)) ,@end-dots))))
  9338. ((pair? (cadr use-expr))
  9339. (if (symbol? (caadr use-expr))
  9340. (let-temporarily ((target-line-length 120))
  9341. ;; (let () (display 32) (define x 2) (define (f101 y) (+ x y)) (display 41) (f101 2)) ->
  9342. ;; (... (define f101 (let ((x 2)) (lambda (y) (+ x y)))) ...)
  9343. (lint-format "perhaps move ~A into ~A's closure: ~A" caller name (caadr use-expr)
  9344. (truncated-lists->string `(... ,expr ,use-expr ,@end-dots)
  9345. `(... (define ,(caadr use-expr)
  9346. (,letx ((,name ,(caddr expr)))
  9347. (lambda ,(cdadr use-expr)
  9348. ,@(cddr use-expr))))
  9349. ,@end-dots))))))
  9350. ((and (symbol? (cadr use-expr))
  9351. (pair? (cddr use-expr)))
  9352. (let-temporarily ((target-line-length 120))
  9353. (if (and (pair? (caddr use-expr))
  9354. (eq? (caaddr use-expr) 'lambda))
  9355. ;; (let () (display 34) (define x 2) (define f101 (lambda (y) (+ x y))) (display 41) (f101 2))
  9356. ;; (... (define f101 (let ((x 2)) (lambda (y) (+ x y)))) ...)
  9357. (lint-format "perhaps move ~A into ~A's closure: ~A" caller name (cadr use-expr)
  9358. (truncated-lists->string `(... ,expr ,use-expr ,@end-dots)
  9359. `(... (define ,(cadr use-expr)
  9360. (,letx ((,name ,(caddr expr)))
  9361. ,(caddr use-expr)))
  9362. ,@end-dots)))
  9363. ;; (... (define lib (r file)) (define exports (caddr lib)) ...) ->
  9364. ;; (... (define exports (let ((lib (r file))) (caddr lib))) ...)
  9365. (lint-format "the scope of ~A could be reduced: ~A" caller name
  9366. (truncated-lists->string `(... ,expr ,use-expr ,@end-dots)
  9367. `(... (define ,(cadr use-expr)
  9368. (,letx ((,name ,(caddr expr)))
  9369. ,(caddr use-expr)))
  9370. ,@end-dots))))))))
  9371. (when (and (> len 3)
  9372. (< k last-ref (+ k 3)) ; larger cases happen very rarely -- 3 or 4 altogether
  9373. (pair? (list-ref body (+ k 1)))
  9374. (pair? (list-ref body (+ k 2))))
  9375. (let ((end-dots (if (< last-ref (- len 1)) '(...) ()))
  9376. (letx (if (tree-member name (cddr expr)) 'letrec 'let))
  9377. (seen-earlier (or (var-member name env)
  9378. (do ((s body (cdr s)))
  9379. ((or (eq? s q)
  9380. (and (pair? (car s))
  9381. (tree-memq name (car s))))
  9382. (not (eq? s q)))))))
  9383. (unless seen-earlier
  9384. (let ((use-expr1 (list-ref body (+ k 1)))
  9385. (use-expr2 (list-ref body (+ k 2))))
  9386. (if (not (or (tree-set-member '(define lambda) use-expr1)
  9387. (tree-set-member '(define lambda) use-expr2)))
  9388. ;; (... (define f101 (lambda (y) (+ x y))) (display 41) (f101 2)) ->
  9389. ;; (... (let ((f101 (lambda (y) (+ x y)))) (display 41) (f101 2)))
  9390. (lint-format "the scope of ~A could be reduced: ~A" caller name
  9391. (let-temporarily ((target-line-length 120))
  9392. (truncated-lists->string `(... ,expr ,use-expr1 ,use-expr2 ,@end-dots)
  9393. `(... (,letx ((,name ,(caddr expr)))
  9394. ,use-expr1
  9395. ,use-expr2)
  9396. ,@end-dots)))))))))))
  9397. (when (tree-memq name (car p))
  9398. (set! last-ref i)))))))))
  9399. (when (= suggest made-suggestion)
  9400. ;; look for define+binding-expr at end and combine
  9401. (do ((prev-f #f)
  9402. (fs body (cdr fs)))
  9403. ((not (pair? fs)))
  9404. (let ((f (car fs)))
  9405. ;; define can come after the use, and in an open body can be equivalent to set!:
  9406. ;; (let () (if x (begin (define y 12) (do ((i 0 (+ i 1))) ((= i y)) (f i))) (define y 21)) y)
  9407. ;; (let () (define (f x) (+ y x)) (if z (define y 12) (define y 1)) (f 12))
  9408. ;; so we can't do this check in walk-open-body
  9409. ;;
  9410. ;; define + do -- if cadr prev-f not used in do inits, fold into do, else use let
  9411. ;; the let case is semi-redundant (it's already reported elsewhere)
  9412. (when (and (pair? prev-f)
  9413. (pair? f)
  9414. (eq? (car prev-f) 'define)
  9415. (symbol? (cadr prev-f))
  9416. (not (hash-table-ref other-identifiers (cadr prev-f))) ; (cadr prev-f) already ref'd, so it's a member of env
  9417. (or (null? (cdr fs))
  9418. (not (tree-memq (cadr prev-f) (cdr fs)))))
  9419. (if (eq? (car f) 'do)
  9420. ;; (... (define z (f x)) (do ((i z (+ i 1))) ((= i 3)) (display (+ z i))) ...) -> (do ((i (f x) (+ i 1))) ((= i 3)) (display (+ z i)))
  9421. (lint-format "perhaps ~A" caller
  9422. (lists->string `(... ,prev-f ,f ...)
  9423. (if (any? (lambda (p)
  9424. (tree-memq (cadr prev-f) (cadr p)))
  9425. (cadr f))
  9426. (if (and (eq? (cadr prev-f) (cadr (caadr f)))
  9427. (null? (cdadr f)))
  9428. `(do ((,(caaadr f) ,(caddr prev-f) ,(caddr (caadr f)))) ,@(cddr f))
  9429. `(let (,(cdr prev-f)) ,f))
  9430. `(do (,(cdr prev-f)
  9431. ,@(cadr f))
  9432. ,@(cddr f)))))
  9433. ;; just changing define -> let seems officious, though it does reduce (cadr prev-f)'s scope
  9434. (if (and (or (and (eq? (car f) 'let)
  9435. (not (tree-memq (cadr prev-f) (cadr f))))
  9436. (eq? (car f) 'let*))
  9437. (not (symbol? (cadr f))))
  9438. (lint-format "perhaps ~A" caller
  9439. (lists->string
  9440. `(... ,prev-f ,f ,@(if (null? (cdr fs)) () '(...)))
  9441. `(... (,(car f) (,(cdr prev-f) ,@(cadr f)) ...) ,@(if (null? (cdr fs)) () '(...))))))))
  9442. (set! prev-f f))))))))
  9443. ;; definer as last in body is rare outside let-syntax, and tricky -- only one clear optimizable case found
  9444. (lint-walk-open-body caller head body env))
  9445. (define (lint-walk-open-body caller head body env)
  9446. ;; walk a body (a list of forms, the value of the last of which might be returned)
  9447. (if (not (proper-list? body))
  9448. (lint-format "stray dot? ~A" caller (truncated-list->string body))
  9449. (let ((prev-f #f)
  9450. (old-current-form lint-current-form)
  9451. (old-mid-form lint-mid-form)
  9452. (prev-len 0)
  9453. (f-len 0)
  9454. (repeats 0)
  9455. (start-repeats body)
  9456. (repeat-arg 0)
  9457. (dpy-f #f)
  9458. (dpy-start #f)
  9459. (rewrote-already #f)
  9460. (len (length body)))
  9461. (if (eq? head 'do) (set! len (+ len 1))) ; last form in do body is not returned
  9462. (do ((fs body (cdr fs))
  9463. (ctr 0 (+ ctr 1)))
  9464. ((not (pair? fs)))
  9465. (let* ((f (car fs))
  9466. (f-func (and (pair? f) (car f))))
  9467. (when (and (pair? f)
  9468. (pair? (cdr f)))
  9469. (if (eq? f-func 'define)
  9470. (let ((vname (if (symbol? (cadr f))
  9471. (cadr f)
  9472. (and (pair? (cadr f))
  9473. (symbol? (caadr f))
  9474. (caadr f)))))
  9475. ;; if already in env, check shadowing request
  9476. (if (and *report-shadowed-variables*
  9477. (var-member vname env))
  9478. ;; (let ((f33 33)) (define f33 4) (g f33 1))
  9479. (lint-format "~A variable ~A in ~S shadows an earlier declaration" caller head vname f))))
  9480. ;; mid-body defines happen by the million, so resistance is futile
  9481. ;; -------- repeated if/when etc --------
  9482. (when (and (pair? prev-f) ; (if A ...) (if A ...) -> (when A ...) or equivalents
  9483. (memq (car prev-f) '(if when unless))
  9484. (memq f-func '(if when unless))
  9485. (pair? (cdr prev-f))
  9486. (pair? (cddr f)) ; possible broken if statement
  9487. (pair? (cddr prev-f)))
  9488. (define (tree-change-member set tree)
  9489. (and (pair? tree)
  9490. (not (eq? (car tree) 'quote))
  9491. (or (and (eq? (car tree) 'set!)
  9492. (memq (cadr tree) set))
  9493. (tree-change-member set (car tree))
  9494. (tree-change-member set (cdr tree)))))
  9495. (let ((test1 (cadr prev-f))
  9496. (test2 (cadr f)))
  9497. ;; (if A...) (if (not A)...) happens very rarely -- only two rewritable hits
  9498. (let ((equal-tests ; test1 = test2 [check for side-effects already]
  9499. (lambda ()
  9500. (if (and (pair? (caddr prev-f))
  9501. (escape? (caddr prev-f) env))
  9502. ;; (begin (if x (error 'oops)) (if x y)) -> begin: x is #f in (if x y) -- this never happens
  9503. (lint-format "~A is #f in ~A" caller
  9504. test2 (truncated-list->string f)))
  9505. ;; (... (if (and A B) (f C)) (if (and B A) (g E) (h F)) ...) -> (... (if (and A B) (begin (f C) (g E)) (begin (h F))) ...)
  9506. (lint-format "perhaps ~A" caller
  9507. (lists->string
  9508. `(... ,prev-f ,f ...)
  9509. (if (eq? f-func 'if)
  9510. (if (and (null? (cdddr prev-f))
  9511. (null? (cdddr f)))
  9512. ;; if (null (cdr fs)) we have to make sure the returned value is not changed by our rewrite
  9513. ;; but when/unless return their last value in s7 (or #<unspecified>), so I think this is ok
  9514. (if (and (pair? test1)
  9515. (eq? (car test1) 'not))
  9516. `(... (unless ,(cadr test1)
  9517. ,@(unbegin (caddr prev-f))
  9518. ,@(unbegin (caddr f))) ...)
  9519. `(... (when ,test1
  9520. ,@(unbegin (caddr prev-f))
  9521. ,@(unbegin (caddr f))) ...))
  9522. `(... (if ,test1
  9523. (begin
  9524. ,@(unbegin (caddr prev-f))
  9525. ,@(unbegin (caddr f)))
  9526. (begin
  9527. ,@(if (pair? (cdddr prev-f)) (unbegin (cadddr prev-f)) ())
  9528. ,@(if (pair? (cdddr f)) (unbegin (cadddr f)) ())))
  9529. ...))
  9530. `(,f-func ,test1 ; f-func = when|unless
  9531. ,@(cddr prev-f)
  9532. ,@(cddr f)))))))
  9533. (test1-in-test2
  9534. (lambda ()
  9535. (if (null? (cddr test2))
  9536. (set! test2 (cadr test2)))
  9537. ;; (... (if A (f B)) (when (and A C) (g D) (h E)) ...) -> (... (when A (f B) (when C (g D) (h E))) ...)
  9538. (lint-format "perhaps ~A" caller
  9539. (lists->string `(... ,prev-f ,f ...)
  9540. (if (or (null? (cdddr prev-f))
  9541. (eq? (car prev-f) 'when)) ; so prev-f is when or 1-arm if (as is f)
  9542. `(... (when ,test1
  9543. ,@(cddr prev-f)
  9544. (when ,test2
  9545. ,@(cddr f)))
  9546. ,@(if (null? (cdr fs)) () '(...)))
  9547. ;; prev-f is 2-arm if and f is when or 1-arm if (the other case is too ugly)
  9548. `(... (if ,test1
  9549. (begin
  9550. ,(caddr prev-f)
  9551. (when ,test2
  9552. ,@(cddr f)))
  9553. ,@(cdddr prev-f)) ...))))))
  9554. (test2-in-test1
  9555. (lambda ()
  9556. (if (null? (cddr test1))
  9557. (set! test1 (cadr test1)))
  9558. ;; (... (if (and A B) (f C)) (if A (g E)) ...) -> (... (when A (when B (f C)) (g E)))
  9559. (lint-format "perhaps ~A" caller
  9560. (lists->string `(... ,prev-f ,f ...)
  9561. (if (or (null? (cdddr f))
  9562. (eq? f-func 'when)) ; so f is when or 1-arm if (as is prev-f)
  9563. `(... (when ,test2
  9564. (when ,test1
  9565. ,@(cddr prev-f))
  9566. ,@(cddr f))
  9567. ,@(if (null? (cdr fs)) () '(...)))
  9568. ;; f is 2-arm if and prev-f is when or 1-arm if
  9569. `(... (if ,test2
  9570. (begin
  9571. (when ,test1
  9572. ,@(cddr prev-f))
  9573. ,(caddr f))
  9574. ,(cadddr f))
  9575. ,@(if (null? (cdr fs)) () '(...)))))))))
  9576. (cond ((equal? test1 test2)
  9577. (if (and (eq? f-func (car prev-f))
  9578. (not (side-effect? test1 env))
  9579. (not (tree-change-member (gather-symbols test1) (cdr prev-f))))
  9580. (equal-tests)))
  9581. ((or (eq? f-func 'unless)
  9582. (eq? (car prev-f) 'unless))) ; too hard!
  9583. ;; look for test1 as member of test2 (so we can use test1 as the outer test)
  9584. ((and (pair? test2)
  9585. (eq? (car test2) 'and)
  9586. (member test1 (cdr test2))
  9587. (or (eq? f-func 'when) ; f has to be when or 1-arm if
  9588. (null? (cdddr f)))
  9589. (or (pair? (cdr fs)) ; if prev-f has false branch, we have to ignore the return value of f
  9590. (eq? (car prev-f) 'when)
  9591. (null? (cdddr prev-f)))
  9592. (not (side-effect? test2 env))
  9593. (not (tree-change-member (gather-symbols test1) (cddr prev-f))))
  9594. (set! test2 (remove test1 test2))
  9595. (test1-in-test2))
  9596. ;; look for test2 as member of test1
  9597. ((and (pair? test1)
  9598. (eq? (car test1) 'and)
  9599. (member test2 (cdr test1))
  9600. (or (eq? (car prev-f) 'when) ; prev-f has to be when or 1-arm if
  9601. (null? (cdddr prev-f)))
  9602. (not (side-effect? test1 env))
  9603. (not (tree-change-member (gather-symbols test2) (cddr prev-f))))
  9604. (set! test1 (remove test2 test1))
  9605. (test2-in-test1))
  9606. ;; look for some intersection of test1 and test2
  9607. ((and (pair? test1)
  9608. (pair? test2)
  9609. (eq? (car test1) 'and)
  9610. (eq? (car test2) 'and)
  9611. (not (side-effect? test1 env))
  9612. (not (side-effect? test2 env))
  9613. (not (tree-change-member (gather-symbols test2) (cddr prev-f))))
  9614. (let ((intersection ())
  9615. (new-test1 ())
  9616. (new-test2 ()))
  9617. (for-each (lambda (tst)
  9618. (if (member tst test2)
  9619. (set! intersection (cons tst intersection))
  9620. (set! new-test1 (cons tst new-test1))))
  9621. (cdr test1))
  9622. (for-each (lambda (tst)
  9623. (if (not (member tst test1))
  9624. (set! new-test2 (cons tst new-test2))))
  9625. (cdr test2))
  9626. (when (pair? intersection)
  9627. (if (null? new-test1)
  9628. (if (null? new-test2)
  9629. (begin
  9630. (set! test1 `(and ,@(reverse intersection)))
  9631. (equal-tests))
  9632. (when (and (or (eq? f-func 'when)
  9633. (null? (cdddr f)))
  9634. (or (pair? (cdr fs))
  9635. (eq? (car prev-f) 'when)
  9636. (null? (cdddr prev-f))))
  9637. (set! test1 `(and ,@(reverse intersection)))
  9638. (set! test2 `(and ,@(reverse new-test2)))
  9639. (test1-in-test2)))
  9640. (if (null? new-test2)
  9641. (when (or (eq? (car prev-f) 'when)
  9642. (null? (cdddr prev-f)))
  9643. (set! test2 `(and ,@(reverse intersection)))
  9644. (set! test1 `(and ,@(reverse new-test1)))
  9645. (test2-in-test1))
  9646. (when (and (or (eq? f-func 'when)
  9647. (null? (cdddr f)))
  9648. (or (eq? (car prev-f) 'when)
  9649. (null? (cdddr prev-f))))
  9650. ;; (... (if (and A B) (f C)) (when (and B C) (g E)) ...) -> (... (when B (when A (f C)) (when C (g E))))
  9651. (lint-format "perhaps ~A" caller
  9652. (let ((outer-test (if (null? (cdr intersection))
  9653. (car intersection)
  9654. `(and ,@(reverse intersection)))))
  9655. (set! new-test1 (if (null? (cdr new-test1))
  9656. (car new-test1)
  9657. `(and ,@(reverse new-test1))))
  9658. (set! new-test2 (if (null? (cdr new-test2))
  9659. (car new-test2)
  9660. `(and ,@(reverse new-test2))))
  9661. (lists->string `(... ,prev-f ,f ...)
  9662. `(... (when ,outer-test
  9663. (when ,new-test1
  9664. ,@(cddr prev-f))
  9665. (when ,new-test2
  9666. ,@(cddr f)))
  9667. ,@(if (null? (cdr fs)) () '(...)))))))))))))))))
  9668. ;; --------
  9669. ;; check for repeated calls, but only one arg currently can change (more args = confusing separation in code)
  9670. (let ((feq (and (pair? prev-f)
  9671. (pair? f)
  9672. (eq? f-func (car prev-f))
  9673. (or (equal? (cdr f) (cdr prev-f))
  9674. (do ((fp (cdr f) (cdr fp))
  9675. (pp (cdr prev-f) (cdr pp))
  9676. (i 1 (+ i 1)))
  9677. ((or (and (null? pp)
  9678. (null? fp))
  9679. (not (pair? pp))
  9680. (not (pair? fp))
  9681. (if (= i repeat-arg) ; ignore the arg that's known to be changing
  9682. (side-effect? (car pp) env)
  9683. (and (not (equal? (car pp) (car fp)))
  9684. (or (positive? repeat-arg)
  9685. (and (set! repeat-arg i) ; call this one the changer
  9686. #f)))))
  9687. (and (null? pp)
  9688. (null? fp))))))))
  9689. (if feq
  9690. (set! repeats (+ repeats 1)))
  9691. (when (or (not feq)
  9692. (= ctr (- len 1))) ; this assumes we're not returning the last value?
  9693. (when (and (> repeats 2)
  9694. (not (hash-table-ref syntaces (car prev-f)))) ; macros should be ok here if args are constants
  9695. (if (zero? repeat-arg) ; simple case -- all exprs are identical
  9696. (let ((step 'i))
  9697. (if (tree-member step prev-f)
  9698. (set! step (find-unique-name prev-f)))
  9699. (lint-format "perhaps ~A... ->~%~NC(do ((~A 0 (+ ~A 1))) ((= ~A ~D)) ~A)" caller
  9700. (truncated-list->string prev-f)
  9701. pp-left-margin #\space
  9702. step step step (+ repeats 1)
  9703. prev-f))
  9704. (let ((fs-end (if (not feq) fs (cdr fs)))
  9705. (args ())
  9706. (constants? #t)
  9707. (func-name (car prev-f))
  9708. (new-arg (if (tree-member 'arg prev-f)
  9709. (find-unique-name prev-f)
  9710. 'arg)))
  9711. (do ((p start-repeats (cdr p)))
  9712. ((eq? p fs-end))
  9713. (set! args (cons (list-ref (car p) repeat-arg) args))
  9714. (if constants? (set! constants? (code-constant? (car args)))))
  9715. (let ((func (if (and (= repeat-arg 1)
  9716. (null? (cddar start-repeats)))
  9717. func-name
  9718. `(lambda (,new-arg)
  9719. ,(let ((call (copy prev-f)))
  9720. (list-set! call repeat-arg new-arg)
  9721. call)))))
  9722. (if constants?
  9723. (lint-format "perhaps ~A... ->~%~NC(for-each ~S '(~{~S~^ ~}))" caller
  9724. (truncated-list->string (car start-repeats))
  9725. pp-left-margin #\space
  9726. func
  9727. (map unquoted (reverse args)))
  9728. (let ((v (var-member func-name env)))
  9729. (if (or (and (var? v)
  9730. (memq (var-ftype v) '(define define* lambda lambda*)))
  9731. (procedure? (symbol->value func-name *e*)))
  9732. ;; (let () (write-byte 0) (write-byte 1) (write-byte 2) (write-byte 3) (write-byte 4)) ->
  9733. ;; (for-each write-byte '(0 1 2 3 4))
  9734. (lint-format "perhaps ~A... ->~%~NC(for-each ~S (vector ~{~S~^ ~}))" caller
  9735. ;; vector rather than list because it is easier on the GC (list copies in s7)
  9736. (truncated-list->string (car start-repeats))
  9737. pp-left-margin #\space
  9738. func
  9739. (reverse args))
  9740. (if (not (or (var? v)
  9741. (macro? (symbol->value func-name *e*))))
  9742. ;; (let () (writ 0) (writ 1) (writ 2) (writ 3) (writ (* x 2))) -> (for-each writ (vector 0 1 2 3 (* x 2)))
  9743. (lint-format "assuming ~A is not a macro, perhaps ~A" caller
  9744. func-name
  9745. (lists->string (list '... (car start-repeats) '...)
  9746. `(for-each ,func (vector ,@(reverse args)))))))))))))
  9747. (set! repeats 0)
  9748. (set! repeat-arg 0)
  9749. (set! start-repeats fs)))
  9750. ;; --------
  9751. (if (pair? f)
  9752. (begin
  9753. (set! f-len (length f))
  9754. (if (eq? f-func 'begin)
  9755. (lint-format "redundant begin: ~A" caller (truncated-list->string f))))
  9756. (begin
  9757. (if (symbol? f)
  9758. (set-ref f caller f env))
  9759. (set! f-len 0)))
  9760. ;; set-car! + set-cdr! here is usually "clever" code assuming eq?ness, so we can't rewrite it using cons
  9761. ;; but copy does not create a new cons... [if at end of body, the return values will differ]
  9762. (when (= f-len prev-len 3)
  9763. (when (and (memq f-func '(set-car! set-cdr!)) ; ...(set-car! x (car y)) (set-cdr! x (cdr y))... -> (copy y x)
  9764. (memq (car prev-f) '(set-car! set-cdr!))
  9765. (not (eq? (car prev-f) f-func))
  9766. (equal? (cadr f) (cadr prev-f)))
  9767. (let ((ncar (caddr (if (eq? f-func 'set-car!) f prev-f)))
  9768. (ncdr (caddr (if (eq? f-func 'set-car!) prev-f f))))
  9769. (if (and (pair? ncar)
  9770. (eq? (car ncar) 'car)
  9771. (pair? ncdr)
  9772. (eq? (car ncdr) 'cdr)
  9773. (equal? (cadr ncar) (cadr ncdr)))
  9774. (lint-format "perhaps ~A~A ~A~A -> ~A" caller
  9775. (if (= ctr 0) "" "...")
  9776. (truncated-list->string prev-f)
  9777. (truncated-list->string f)
  9778. (if (= ctr (- len 1)) "" "...")
  9779. `(copy ,(cadr ncar) ,(cadr f))))))
  9780. ;; successive if's that can be combined into case
  9781. ;; else in last if could be accommodated as well
  9782. (when (and (not rewrote-already)
  9783. (eq? f-func 'if)
  9784. (eq? (car prev-f) 'if)
  9785. (pair? (cadr f))
  9786. (pair? (cadr prev-f))
  9787. (= (length f) 3)
  9788. (= (length prev-f) 3)
  9789. (memq (caadr prev-f) '(eq? eqv? = char=?)) ; not memx
  9790. (memq (caadr f) '(eq? eqv? = char=?)))
  9791. (let ((a1 (cadadr prev-f))
  9792. (a2 (caddr (cadr prev-f)))
  9793. (b1 (cadadr f))
  9794. (b2 (caddr (cadr f)))) ; other possibilities are never hit
  9795. (when (and (equal? a1 b1)
  9796. (code-constant? a2)
  9797. (code-constant? b2)
  9798. (not (tree-change-member (list a1) (cddr prev-f)))) ; or any symbol in a1?
  9799. (set! rewrote-already #t)
  9800. ;; (... (if (= x 1) (display y)) (if (= x 2) (f y)) ...) -> (case x ((1) (display y)) ((2) (f y)) ((3) (display z)))
  9801. (lint-format "perhaps ~A" caller
  9802. (lists->string `(... ,prev-f ,f ...)
  9803. `(case ,a1
  9804. ((,(unquoted a2)) ,@(unbegin (caddr prev-f)))
  9805. ((,(unquoted b2)) ,@(unbegin (caddr f)))
  9806. ,@(do ((more ())
  9807. (nfs (cdr fs) (cdr nfs)))
  9808. ((let ((nf (if (pair? nfs) (car nfs) ())))
  9809. (not (and (pair? nf)
  9810. (eq? (car nf) 'if)
  9811. (= (length nf) 3)
  9812. (pair? (cadr nf))
  9813. (memq (caadr nf) '(eq? eqv? = char=?))
  9814. (equal? a1 (cadadr nf))
  9815. (code-constant? (caddr (cadr nf))))))
  9816. ;; maybe add (not (tree-change-member (list a1) (cddr last-f)))
  9817. ;; but it never is needed
  9818. (reverse more))
  9819. (if (pair? nfs)
  9820. (set! more (cons (cons (list (unquoted (caddr (cadar nfs))))
  9821. (unbegin (caddar nfs)))
  9822. more))))))))))
  9823. (when (and (eq? f-func 'set!)
  9824. (eq? (car prev-f) 'set!))
  9825. (let ((arg1 (caddr prev-f))
  9826. (arg2 (caddr f))
  9827. (settee (cadr f)))
  9828. (if (and (or (and (equal? settee arg1) ; (set! x y) (set! y x)
  9829. (equal? arg2 (cadr prev-f)))
  9830. (and (equal? settee (cadr prev-f)) ; (set! x y) (set! x y)
  9831. (equal? arg1 arg2)))
  9832. (not (tree-equal-member settee arg2)))
  9833. (lint-format "this pair of set!s looks odd: ~A" caller
  9834. `(... ,prev-f ,f ...)))
  9835. (cond ((not (eq? settee (cadr prev-f)))
  9836. (if (and (symbol? (cadr prev-f)) ; (set! x (A)) (set! y (A)) -> (set! x (A)) (set! y x)
  9837. (pair? arg1) ; maybe more trouble than it's worth
  9838. (equal? arg1 arg2)
  9839. (not (eq? (car arg1) 'quote))
  9840. (hash-table-ref no-side-effect-functions (car arg1))
  9841. (not (tree-unquoted-member (cadr prev-f) arg1))
  9842. (not (side-effect? arg1 env))
  9843. (not (maker? arg1)))
  9844. (lint-format "perhaps ~A" caller (lists->string f `(set! ,settee ,(cadr prev-f))))))
  9845. ((not (and (pair? arg2) ; (set! x 0) (set! x 1) -> "this could be omitted: (set! x 0)"
  9846. (tree-unquoted-member settee arg2)))
  9847. (if (not (or (side-effect? arg1 env)
  9848. (side-effect? arg2 env)))
  9849. (lint-format "this could be omitted: ~A" caller prev-f)))
  9850. ((and (pair? arg1) ; (set! x (cons 1 z)) (set! x (cons 2 x)) -> (set! x (cons 2 (cons 1 z)))
  9851. (pair? arg2)
  9852. (eq? (car arg1) 'cons)
  9853. (eq? (car arg2) 'cons)
  9854. (eq? settee (caddr arg2))
  9855. (not (eq? settee (cadr arg2))))
  9856. (lint-format "perhaps ~A ~A -> ~A" caller
  9857. prev-f f
  9858. `(set! ,settee (cons ,(cadr arg2) (cons ,@(cdr arg1))))))
  9859. ((and (pair? arg1) ; (set! x (append x y)) (set! x (append x z)) -> (set! x (append x y z))
  9860. (pair? arg2)
  9861. (eq? (car arg1) 'append)
  9862. (eq? (car arg2) 'append)
  9863. (eq? settee (cadr arg1))
  9864. (eq? settee (cadr arg2))
  9865. (not (tree-memq settee (cddr arg1)))
  9866. (not (tree-memq settee (cddr arg2))))
  9867. (lint-format "perhaps ~A ~A -> ~A" caller
  9868. prev-f f
  9869. `(set! ,settee (append ,settee ,@(cddr arg1) ,@(cddr arg2)))))
  9870. ((and (= (tree-count1 settee arg2 0) 1) ; (set! x y) (set! x (+ x 1)) -> (set! x (+ y 1))
  9871. (or (not (pair? arg1))
  9872. (< (tree-leaves arg1) 5)))
  9873. (lint-format "perhaps ~A ~A ->~%~NC~A" caller
  9874. prev-f f pp-left-margin #\space
  9875. (object->string `(set! ,settee ,(tree-subst arg1 settee arg2)))))))))
  9876. (if (< ctr (- len 1))
  9877. (begin ; f is not the last form, so its value is ignored
  9878. (if (and (escape? f env)
  9879. (pair? (cdr fs)) ; do special case
  9880. (every? (lambda (arg)
  9881. (not (and (symbol? arg)
  9882. (let ((v (var-member arg env)))
  9883. (and (var? v)
  9884. (eq? (var-initial-value v) :call/cc))))))
  9885. (cdr f)))
  9886. (if (= ctr (- len 2))
  9887. ;; (let () (error 'oops "an error") #t)
  9888. (lint-format "~A makes this pointless: ~A" caller
  9889. (truncated-list->string f)
  9890. (truncated-list->string (cadr fs)))
  9891. ;; (begin (stop) (exit 6) (print 4) (stop))
  9892. (lint-format "~A makes the rest of the body unreachable: ~A" caller
  9893. (truncated-list->string f)
  9894. (truncated-list->string (list '... (cadr fs) '...)))))
  9895. (check-returns caller f env))
  9896. ;; here f is the last form in the body
  9897. (when (and (pair? prev-f)
  9898. (pair? (cdr prev-f)))
  9899. (case (car prev-f)
  9900. ((display write write-char write-byte)
  9901. (if (and (equal? f (cadr prev-f))
  9902. (not (side-effect? f env)))
  9903. ;; (cond ((= x y) y) (else (begin (display x) x)))
  9904. (lint-format "~A returns its first argument, so this could be omitted: ~A" caller
  9905. (car prev-f) (truncated-list->string f))))
  9906. ((vector-set! float-vector-set! int-vector-set! byte-vector-set!
  9907. string-set! list-set! hash-table-set! let-set! set-car! set-cdr!)
  9908. (if (equal? f (list-ref prev-f (- (length prev-f) 1)))
  9909. ;; (begin (vector-set! x 0 (* y 2)) (* y 2))
  9910. (lint-format "~A returns the new value, so this could be omitted: ~A" caller
  9911. (car prev-f) (truncated-list->string f)))
  9912. (if (and (pair? f)
  9913. (pair? (cdr f))
  9914. (eq? (cadr prev-f) (cadr f))
  9915. (not (code-constant? (cadr f)))
  9916. (case (car prev-f)
  9917. ((vector-set! float-vector-set! int-vector-set!)
  9918. (memq f-func '(vector-ref float-vector-ref int-vector-ref)))
  9919. ((list-set!)
  9920. (eq? f-func 'list-ref))
  9921. ((string-set!)
  9922. (eq? f-func 'string-ref))
  9923. ((set-car!)
  9924. (eq? f-func 'car))
  9925. ((set-cdr!)
  9926. (eq? f-func 'cdr))
  9927. (else #f))
  9928. (or (memq f-func '(car cdr)) ; no indices
  9929. (and (pair? (cddr f)) ; for the others check that indices match
  9930. (equal? (caddr f) (caddr prev-f))
  9931. (pair? (cdddr prev-f))
  9932. (not (pair? (cddddr prev-f)))
  9933. (not (pair? (cdddr f)))
  9934. (not (side-effect? (caddr f) env)))))
  9935. ;; (let ((x (list 1 2))) (set-car! x 3) (car x))
  9936. (lint-format "~A returns the new value, so this could be omitted: ~A" caller
  9937. (car prev-f) (truncated-list->string f))))
  9938. ((copy)
  9939. (if (or (and (null? (cddr prev-f))
  9940. (equal? (cadr prev-f) f))
  9941. (and (pair? (cddr prev-f))
  9942. (null? (cdddr prev-f))
  9943. (equal? (caddr prev-f) f)))
  9944. (lint-format "~A returns the new value, so ~A could be omitted" caller
  9945. (truncated-list->string prev-f)
  9946. (truncated-list->string f))))
  9947. ((set! define define* define-macro define-constant define-macro*
  9948. defmacro defmacro* define-expansion define-bacro define-bacro*)
  9949. (cond ((not (and (pair? (cddr prev-f)) ; (set! ((L 1) 2)) an error, but lint should keep going
  9950. (or (and (equal? (caddr prev-f) f) ; (begin ... (set! x (...)) (...))
  9951. (not (side-effect? f env)))
  9952. (and (symbol? f) ; (begin ... (set! x ...) x)
  9953. (eq? f (cadr prev-f))) ; also (begin ... (define x ...) x)
  9954. (and (not (eq? (car prev-f) 'set!))
  9955. (pair? (cadr prev-f)) ; (begin ... (define (x...)...) x)
  9956. (eq? f (caadr prev-f)))))))
  9957. ((not (memq (car prev-f) '(define define*)))
  9958. (lint-format "~A returns the new value, so this could be omitted: ~A" caller
  9959. (car prev-f) (truncated-list->string f)))
  9960. ((symbol? (cadr prev-f))
  9961. (lint-format "perhaps omit ~A and return ~A" caller
  9962. (cadr prev-f)
  9963. (caddr prev-f)))
  9964. ((= (tree-count2 f body 0) 2)
  9965. ;; (let () (define (f1 x) (+ x 1)) f1) -> (lambda (x) ...)
  9966. (lint-format "perhaps omit ~A, and change ~A" caller
  9967. f
  9968. (lists->string `(,(car prev-f) ,(cadr prev-f) ...)
  9969. `(,(if (eq? (car prev-f) 'define) 'lambda 'lambda*)
  9970. ,(cdadr prev-f)
  9971. ...))))
  9972. (else (lint-format "~A returns the new value, so this could be omitted: ~A" caller
  9973. (car prev-f) f)))))))
  9974. ; possibly still not right if letrec?
  9975. ;; needs f fs prev-f dpy-f dpy-start ctr len
  9976. ;; trap lint-format
  9977. (let ((dpy-case (and (pair? f)
  9978. (memq f-func '(display write newline write-char write-string))))) ; flush-output-port?
  9979. (when (and dpy-case
  9980. (not dpy-start))
  9981. (set! dpy-f fs)
  9982. (set! dpy-start ctr))
  9983. (when (and (integer? dpy-start)
  9984. (> (- ctr dpy-start) (if dpy-case 1 2))
  9985. (or (= ctr (- len 1))
  9986. (not dpy-case)))
  9987. ;; display sequence starts at dpy-start, goes to ctr (prev-f) unless not dpy-case
  9988. (let ((ctrl-string "")
  9989. (args ())
  9990. (dctr 0)
  9991. (dpy-last (if (not dpy-case) prev-f f))
  9992. (op (write-port (car dpy-f)))
  9993. (exprs (make-list (if dpy-case (- ctr dpy-start -1) (- ctr dpy-start)) ())))
  9994. (define* (gather-format str (arg :unset))
  9995. (set! ctrl-string (string-append ctrl-string str))
  9996. (unless (eq? arg :unset) (set! args (cons arg args))))
  9997. (call-with-exit
  9998. (lambda (done)
  9999. (for-each
  10000. (lambda (d)
  10001. (if (not (equal? (write-port d) op))
  10002. (begin
  10003. (lint-format "unexpected port change: ~A -> ~A in ~A" caller op (write-port d) d) ; ??
  10004. (done)))
  10005. (list-set! exprs dctr d)
  10006. (set! dctr (+ dctr 1))
  10007. (gather-format (display->format d))
  10008. (when (eq? d dpy-last) ; op can be null => send to (current-output-port), return #f or #<unspecified>
  10009. ;; (begin (display x) (newline) (display y) (newline)) -> (format () "~A~%~A~%" x y)
  10010. (lint-format "perhaps ~A" caller (lists->string `(... ,@exprs)
  10011. `(format ,op ,ctrl-string ,@(reverse args))))
  10012. (done)))
  10013. dpy-f))))
  10014. (set! dpy-start #f))
  10015. (unless dpy-case (set! dpy-start #f)))
  10016. (if (and (pair? f)
  10017. (memq head '(defmacro defmacro* define-macro define-macro* define-bacro define-bacro*))
  10018. (tree-member 'unquote f))
  10019. (lint-format "~A probably has too many unquotes: ~A" caller head (truncated-list->string f)))
  10020. (set! prev-f f)
  10021. (set! prev-len f-len)
  10022. (set! lint-current-form f)
  10023. (if (= ctr (- len 1))
  10024. (set! env (lint-walk caller f env))
  10025. (begin
  10026. (set! lint-mid-form f)
  10027. (let ((e (lint-walk caller f env)))
  10028. (if (and (pair? e)
  10029. (not (memq (var-name (car e)) '(:lambda :dilambda))))
  10030. (set! env e)))))
  10031. (set! lint-current-form #f)
  10032. (set! lint-mid-form #f)
  10033. ;; need to put off this ref tick until we have a var for it (lint-walk above)
  10034. (when (and (= ctr (- len 1))
  10035. (pair? f)
  10036. (pair? (cdr f)))
  10037. (if (and (pair? (cadr f))
  10038. (memq f-func '(define define* define-macro define-constant define-macro* define-expansion define-bacro define-bacro*)))
  10039. (set-ref (caadr f) caller f env)
  10040. (if (memq f-func '(defmacro defmacro*))
  10041. (set-ref (cadr f) caller f env))))
  10042. ))
  10043. (set! lint-mid-form old-mid-form)
  10044. (set! lint-current-form old-current-form)))
  10045. env)
  10046. (define (return-walker last func)
  10047. (if (not (pair? last))
  10048. (func last)
  10049. (case (car last)
  10050. ((begin let let* letrec letrec* when unless with-baffle with-let)
  10051. (when (pair? (cdr last))
  10052. (let ((len (length last)))
  10053. (when (positive? len)
  10054. (return-walker (list-ref last (- len 1)) func)))))
  10055. ((if)
  10056. (when (and (pair? (cdr last))
  10057. (pair? (cddr last)))
  10058. (return-walker (caddr last) func)
  10059. (if (pair? (cdddr last))
  10060. (return-walker (cadddr last) func))))
  10061. ((cond)
  10062. (for-each (lambda (c)
  10063. (if (and (pair? c)
  10064. (pair? (cdr c)))
  10065. (return-walker (list-ref c (- (length c) 1)) func)))
  10066. (cdr last)))
  10067. ((case)
  10068. (when (and (pair? (cdr last))
  10069. (pair? (cddr last)))
  10070. (for-each (lambda (c)
  10071. (if (and (pair? c)
  10072. (pair? (cdr c)))
  10073. (return-walker (list-ref c (- (length c) 1)) func)))
  10074. (cddr last))))
  10075. ((do)
  10076. (if (and (pair? (cdr last))
  10077. (pair? (cddr last))
  10078. (pair? (caddr last))
  10079. (pair? (cdaddr last)))
  10080. (return-walker (list-ref (caddr last) (- (length (caddr last)) 1)) func)))
  10081. ((set!)
  10082. (if (and (pair? (cdr last))
  10083. (pair? (cddr last)))
  10084. (func (caddr last))))
  10085. (else (func last)) ; includes quote
  10086. ;; call-with-exit et al also or|and
  10087. ;; or|and -- call return-walker on each entry?
  10088. ;; call-with-exit: walker on last on body, and scan for return func, walker on arg(s...)->values?
  10089. )))
  10090. (define (check-sequence-constant function-name last)
  10091. (return-walker last
  10092. (lambda (in-seq)
  10093. (when (or (not (pair? in-seq))
  10094. (eq? (car in-seq) 'quote))
  10095. (let ((seq (if (and (pair? in-seq)
  10096. (pair? (cdr in-seq))) ; (quote . 1)??
  10097. (cadr in-seq)
  10098. in-seq)))
  10099. (when (and (sequence? seq)
  10100. (not (zero? (length seq))))
  10101. (lint-format "returns ~A constant: ~A~S" function-name ; (define-macro (m a) `(+ 1 a))
  10102. (if (pair? seq)
  10103. (values "a list" "'" seq)
  10104. (values (prettify-checker-unq (->lint-type in-seq)) "" seq)))
  10105. (throw 'sequence-constant-done))))))) ; just report one constant -- the full list is annoying
  10106. (define lint-function-body #f) ; a momentary kludge??
  10107. (define (lint-walk-function-body definer function-name args body env)
  10108. ;; walk function body, with possible doc string at the start
  10109. (when (and (pair? body)
  10110. (pair? (cdr body))
  10111. (string? (car body)))
  10112. (if *report-doc-strings*
  10113. (lint-format "old-style doc string: ~S, in s7 use 'documentation:~%~NC~A" function-name
  10114. (car body) (+ lint-left-margin 4) #\space
  10115. (lint-pp `(define ,function-name
  10116. (let ((documentation ,(car body)))
  10117. (,(if (eq? definer 'define) 'lambda
  10118. (if (eq? definer 'define*) 'lambda*
  10119. definer))
  10120. ,args
  10121. ,@(cdr body)))))))
  10122. (set! body (cdr body))) ; ignore old-style doc-string
  10123. ;; (set! arg ...) never happens as last in body
  10124. ;; but as first in body, it happens ca 100 times
  10125. (if (and (pair? body)
  10126. (pair? (car body))
  10127. (eq? (caar body) 'set!)
  10128. (or (eq? (cadar body) args)
  10129. (and (pair? args)
  10130. (memq (cadar body) args))))
  10131. ;; (define (f21 x y) (set! x 3) (+ y 1))
  10132. (lint-format "perhaps ~A" function-name
  10133. (lists->string (car body) `(let ((,(cadar body) ,(caddar body))) ...))))
  10134. ;; as first in let of body, maybe a half-dozen
  10135. (let ((tag 'yup))
  10136. (catch 'sequence-constant-done
  10137. (lambda ()
  10138. (check-sequence-constant function-name (list-ref body (- (length body) 1))) ; some of these are innocuous -- lambda forms in midst of outer body etc
  10139. (set! tag 'nope))
  10140. (lambda args #f))
  10141. (if (eq? tag 'yup)
  10142. (let ((v (var-member function-name env)))
  10143. (if (var? v)
  10144. (set! (var-retcons v) #t)))))
  10145. (set! lint-function-body body)
  10146. (lint-walk-body function-name definer body env))
  10147. (define (lint-walk-function definer function-name args body form env)
  10148. ;; check out function arguments (adding them to the current env), then walk its body
  10149. ;; first check for (define (hi...) (ho...)) where ho has no opt args (and try to ignore possible string constant doc string)
  10150. (when (eq? definer 'define)
  10151. (let ((bval (if (and (pair? body)
  10152. (string? (car body)))
  10153. (cdr body) ; strip away the (old-style) documentation string
  10154. body)))
  10155. (cond ((not (and (pair? bval) ; not (define (hi a) . 1)!
  10156. (pair? (car bval))
  10157. (null? (cdr bval))
  10158. (symbol? (caar bval))))) ; not (define (hi) ((if #f + abs) 0))
  10159. ((or (equal? args (cdar bval))
  10160. (and (hash-table-ref reversibles (caar bval))
  10161. (equal? args (reverse (cdar bval)))))
  10162. (let* ((cval (caar bval))
  10163. (p (symbol->value cval *e*))
  10164. (ary (arity p)))
  10165. (if (or (procedure? p)
  10166. (let ((e (var-member cval env) ))
  10167. (and e
  10168. (var? e)
  10169. (symbol? (var-ftype e))
  10170. (let ((def (var-initial-value e))
  10171. (e-args (var-arglist e)))
  10172. (and
  10173. (pair? def)
  10174. (memq (var-ftype e) '(define lambda))
  10175. (or (and (null? args)
  10176. (null? e-args))
  10177. (and (symbol? args)
  10178. (symbol? e-args))
  10179. (and (pair? args)
  10180. (pair? e-args)
  10181. (= (length args) (length e-args)))))))))
  10182. (lint-format "~A~A could be (define ~A ~A)" function-name
  10183. (if (and (procedure? p)
  10184. (not (= (car ary) (cdr ary)))
  10185. (not (= (length args) (cdr ary))))
  10186. (format #f "leaving aside ~A's optional arg~P, " cval (- (cdr ary) (length args)))
  10187. "")
  10188. function-name
  10189. function-name
  10190. (if (equal? args (cdar bval))
  10191. cval
  10192. (hash-table-ref reversibles (caar bval))))
  10193. (if (and (null? args) ; perhaps this can be extended to any equal args
  10194. (null? (cdar bval)))
  10195. ;; (define (getservent) (getserv)) -> (define getservent getserv)
  10196. (lint-format "~A could probably be ~A" function-name
  10197. (truncated-list->string form)
  10198. (truncated-list->string `(define ,function-name ,cval)))))))
  10199. ((and (or (symbol? args)
  10200. (and (pair? args)
  10201. (negative? (length args))))
  10202. (eq? (caar bval) 'apply)
  10203. (pair? (cdar bval))
  10204. (symbol? (cadar bval))
  10205. (not (memq (cadar bval) '(and or)))
  10206. (pair? (cddar bval))
  10207. (or (and (eq? args (caddar bval))
  10208. (null? (cdddar bval)))
  10209. (and (pair? args)
  10210. (equal? (cddar bval) (proper-list args)))))
  10211. ;; (define (f1 . x) (apply + x)) -> (define f1 +)
  10212. (lint-format "~A could be (define ~A ~A)" function-name function-name function-name (cadar bval)))
  10213. ((and (hash-table-ref combinable-cxrs (caar bval))
  10214. (pair? (cadar bval)))
  10215. ((lambda* (cr arg)
  10216. (and cr
  10217. (< (length cr) 5)
  10218. (pair? args)
  10219. (null? (cdr args))
  10220. (eq? (car args) arg)
  10221. (let ((f (symbol "c" cr "r")))
  10222. (if (eq? f function-name)
  10223. ;; (define (cadddr l) (caddr (cdr l)))
  10224. (lint-format "this redefinition of ~A is pointless (use (with-let (unlet)...) or #_~A)" definer function-name function-name)
  10225. ;; (define (f1 x) (cdr (car x))) -> (define f1 cdar)
  10226. (lint-format "~A could be (define ~A ~A)" function-name function-name function-name f)))))
  10227. (combine-cxrs (car bval))))
  10228. ((not (and (memq (caar bval) '(list-ref list-tail))
  10229. (pair? (cdar bval))
  10230. (pair? (cddar bval))
  10231. (pair? args)
  10232. (eq? (car args) (cadar bval))
  10233. (null? (cdr args)))))
  10234. ((eq? (caar bval) 'list-ref)
  10235. (case (caddar bval)
  10236. ((0) (lint-format "~A could be (define ~A car)" function-name function-name function-name))
  10237. ((1) (lint-format "~A could be (define ~A cadr)" function-name function-name function-name))
  10238. ((2) (lint-format "~A could be (define ~A caddr)" function-name function-name function-name))
  10239. ((3) (lint-format "~A could be (define ~A cadddr)" function-name function-name function-name))))
  10240. (else
  10241. (case (caddar bval)
  10242. ((1) (lint-format "~A could be (define ~A cdr)" function-name function-name function-name))
  10243. ((2) (lint-format "~A could be (define ~A cddr)" function-name function-name function-name))
  10244. ((3) (lint-format "~A could be (define ~A cdddr)" function-name function-name function-name))
  10245. ((4) (lint-format "~A could be (define ~A cddddr)" function-name function-name function-name)))))))
  10246. (let ((fvar (and (symbol? function-name)
  10247. (make-fvar :name (if (memq definer '(lambda lambda*))
  10248. :lambda
  10249. (if (eq? definer 'dilambda)
  10250. :dilambda
  10251. function-name))
  10252. :ftype definer
  10253. :initial-value form
  10254. :env env
  10255. :arglist (if (memq definer '(lambda lambda*))
  10256. (cadr form)
  10257. ((if (memq definer '(defmacro defmacro*)) caddr cdadr) form))))))
  10258. (when fvar
  10259. (let ((fvar-let (cdr fvar)))
  10260. (set! (fvar-let 'decl)
  10261. (catch #t
  10262. (lambda ()
  10263. (case definer
  10264. ((lambda)
  10265. (set! (fvar-let 'allow-other-keys) #t)
  10266. (eval (list definer (cadr form) #f)))
  10267. ((lambda*)
  10268. (set! (fvar-let 'allow-other-keys) (eq? (last-par (cadr form)) :allow-other-keys))
  10269. (eval (list definer (copy (cadr form)) #f))) ; eval can remove :allow-other-keys!
  10270. ((define*)
  10271. (set! (fvar-let 'allow-other-keys) (eq? (last-par (cdadr form)) :allow-other-keys))
  10272. (eval (list definer (cons '_ (copy (cdadr form))) #f)))
  10273. ((defmacro defmacro*)
  10274. (set! (fvar-let 'allow-other-keys) (or (not (eq? definer 'defmacro*))
  10275. (eq? (last-par (caddr form)) :allow-other-keys)))
  10276. (eval (list definer '_ (caddr form) #f)))
  10277. ((define-constant)
  10278. (set! (fvar-let 'allow-other-keys) #t)
  10279. (eval (list 'define (cons '_ (cdadr form)) #f)))
  10280. (else
  10281. (set! (fvar-let 'allow-other-keys) (or (not (memq definer '(define-macro* define-bacro*)))
  10282. (eq? (last-par (cdadr form)) :allow-other-keys)))
  10283. (eval (list definer (cons '_ (cdadr form)) #f)))))
  10284. (lambda args
  10285. 'error)))))
  10286. (if (null? args)
  10287. (begin
  10288. (if (memq definer '(define* lambda* defmacro* define-macro* define-bacro*))
  10289. (lint-format "~A could be ~A" ; (define* (f1) 32)
  10290. function-name definer
  10291. (symbol (substring (symbol->string definer) 0 (- (length (symbol->string definer)) 1)))))
  10292. (let ((cur-env (if fvar (cons fvar env) env)))
  10293. (let ((nvars (let ((e (lint-walk-function-body definer function-name args body cur-env)))
  10294. (and (not (eq? e cur-env))
  10295. (env-difference function-name e cur-env ())))))
  10296. (if (pair? nvars)
  10297. (report-usage function-name definer nvars cur-env)))
  10298. cur-env))
  10299. (if (not (or (symbol? args)
  10300. (pair? args)))
  10301. (begin
  10302. (lint-format "strange ~A parameter list ~A" function-name definer args)
  10303. env)
  10304. (let ((args-as-vars (if (symbol? args) ; this is getting arg names to add to the environment
  10305. (list (make-var :name args :definer 'parameter))
  10306. (map (lambda (arg)
  10307. (if (symbol? arg)
  10308. (if (memq arg '(:rest :allow-other-keys))
  10309. (values) ; omit :rest and :allow-other-keys
  10310. (make-var :name arg :definer 'parameter))
  10311. (if (not (and (pair? arg)
  10312. (= (length arg) 2)
  10313. (memq definer '(define* lambda* defmacro* define-macro* define-bacro* definstrument define*-public))))
  10314. (begin
  10315. (lint-format "strange parameter for ~A: ~S" function-name definer arg)
  10316. (values))
  10317. (begin
  10318. (if (not (or (cadr arg) ; (define* (f4 (a #f)) a)
  10319. (eq? definer 'define*-public))) ; who knows?
  10320. (lint-format "the default argument value is #f in ~A ~A" function-name definer arg))
  10321. (make-var :name (car arg) :definer 'parameter)))))
  10322. (proper-list args)))))
  10323. (let* ((cur-env (cons (make-var :name :let
  10324. :initial-value form
  10325. :definer definer)
  10326. (append args-as-vars (if fvar (cons fvar env) env))))
  10327. (nvars (let ((e (lint-walk-function-body definer function-name args body cur-env)))
  10328. (and (not (eq? e cur-env))
  10329. (env-difference function-name e cur-env ())))))
  10330. (report-usage function-name definer (append (or nvars ()) args-as-vars) cur-env))
  10331. (when (and (var? fvar)
  10332. (memq definer '(define lambda define-macro)))
  10333. ;; look for unused parameters that are passed a value other than #f
  10334. (let ((set ())
  10335. (unused ()))
  10336. (for-each
  10337. (lambda (arg-var)
  10338. (if (zero? (var-ref arg-var))
  10339. (if (positive? (var-set arg-var))
  10340. (set! set (cons (var-name arg-var) set))
  10341. (if (not (memq (var-name arg-var) '(documentation signature iterator?)))
  10342. (set! unused (cons (var-name arg-var) unused))))))
  10343. args-as-vars)
  10344. (when (or (pair? set)
  10345. (pair? unused))
  10346. (let ((proper-args (args->proper-list args)))
  10347. (let ((sig (var-signature fvar))
  10348. (len (+ (length proper-args) 1)))
  10349. (if (not sig)
  10350. (set! sig (make-list len #t))
  10351. (if (< (length sig) len)
  10352. (set! sig (copy sig (make-list len #t)))))
  10353. (let ((siglist (cdr sig)))
  10354. (for-each
  10355. (lambda (arg)
  10356. (if (memq arg unused)
  10357. (set-car! siglist 'unused-parameter?)
  10358. (if (memq arg set)
  10359. (set-car! siglist 'unused-set-parameter?)))
  10360. (set! siglist (cdr siglist)))
  10361. proper-args))
  10362. (set! (var-signature fvar) sig))))))
  10363. (if fvar
  10364. (cons fvar env)
  10365. env))))))
  10366. (define (check-bool-cond caller form c1 c2 env)
  10367. ;; (cond (x #f) (#t #t)) -> (not x)
  10368. ;; c1/c2 = possibly combined, so in (cond (x #t) (y #t) (else #f)), c1: ((or x y) #t), so -> (or x y)
  10369. (and (pair? c1)
  10370. (= (length c1) 2)
  10371. (pair? c2)
  10372. (pair? (cdr c2))
  10373. (memq (car c2) '(#t else))
  10374. (or (and (boolean? (cadr c1))
  10375. (or (and (null? (cddr c2))
  10376. (boolean? (cadr c2))
  10377. (not (equal? (cadr c1) (cadr c2))) ; handled elsewhere
  10378. (lint-format "perhaps ~A" caller
  10379. (lists->string form (if (eq? (cadr c1) #t)
  10380. (car c1)
  10381. (simplify-boolean `(not ,(car c1)) () () env)))))
  10382. (and (not (cadr c1)) ; (cond (x #f) (else y)) -> (and (not x) y)
  10383. (let ((cc1 (simplify-boolean `(not ,(car c1)) () () env)))
  10384. (lint-format "perhaps ~A" caller
  10385. (lists->string form
  10386. (if (null? (cddr c2))
  10387. `(and ,cc1 ,(cadr c2))
  10388. `(and ,cc1 (begin ,@(cdr c2))))))))
  10389. (and (pair? (car c1)) ; (cond ((null? x) #t) (else y)) -> (or (null? x) y)
  10390. (eq? (return-type (caar c1) env) 'boolean?)
  10391. (lint-format "perhaps ~A" caller
  10392. (lists->string form
  10393. (if (null? (cddr c2))
  10394. `(or ,(car c1) ,(cadr c2))
  10395. `(or ,(car c1) (begin ,@(cdr c2)))))))))
  10396. (and (boolean? (cadr c2))
  10397. (null? (cddr c2))
  10398. (not (equal? (cadr c1) (cadr c2)))
  10399. ;; (cond ((= 3 (length eq)) (caddr eq)) (else #f)) -> (and (= 3 (length eq)) (caddr eq))
  10400. (lint-format "perhaps ~A" caller
  10401. (lists->string form
  10402. (if (cadr c2)
  10403. `(or (not ,(car c1)) ,(cadr c1))
  10404. (if (and (pair? (car c1))
  10405. (eq? (caar c1) 'and))
  10406. (append (car c1) (cdr c1))
  10407. `(and ,@c1)))))))))
  10408. (define (case-branch test eqv-select exprs)
  10409. (case (car test)
  10410. ((eq? eqv? = equal? char=?)
  10411. (if (equal? eqv-select (cadr test))
  10412. `((,(unquoted (caddr test))) ,@exprs)
  10413. `((,(unquoted (cadr test))) ,@exprs)))
  10414. ((memq memv member)
  10415. `(,(unquoted (caddr test)) ,@exprs))
  10416. ((not)
  10417. `((#f) ,@exprs))
  10418. ((null?)
  10419. `((()) ,@exprs))
  10420. ((eof-object?)
  10421. `((#<eof>) ,@exprs))
  10422. ((zero?)
  10423. `((0 0.0) ,@exprs))
  10424. ((boolean?)
  10425. `((#t #f) ,@exprs))
  10426. ((char-ci=?)
  10427. (if (equal? eqv-select (cadr test))
  10428. `(,(list (caddr test) (other-case (caddr test))) ,@exprs)
  10429. `(,(list (cadr test) (other-case (cadr test))) ,@exprs)))
  10430. (else
  10431. `(,(map (lambda (p)
  10432. (case (car p)
  10433. ((eq? eqv? = equal? char=?)
  10434. (unquoted ((if (equal? eqv-select (cadr p)) caddr cadr) p)))
  10435. ((memq memv member) (apply values (caddr p)))
  10436. ((not) #f)
  10437. ((null?) ())
  10438. ((eof-object?) #<eof>)
  10439. ((zero?) (values 0 0.0))
  10440. ((boolean?) (values #t #f))
  10441. ((char-ci=?)
  10442. (if (equal? eqv-select (cadr p))
  10443. (values (caddr p) (other-case (caddr p)))
  10444. (values (cadr p) (other-case (cadr p)))))
  10445. (else (error "oops"))))
  10446. (cdr test))
  10447. ,@exprs))))
  10448. (define (cond->case eqv-select new-clauses)
  10449. `(case ,eqv-select
  10450. ,@(map (lambda (clause)
  10451. (let ((test (car clause))
  10452. (exprs (cdr clause)))
  10453. (if (null? exprs) ; cond returns the test result if no explicit results
  10454. (set! exprs (list #t))) ; but all tests here return a boolean, and we win only if #t?? (memx is an exception)
  10455. (if (memq test '(else #t))
  10456. `(else ,@exprs)
  10457. (case-branch test eqv-select exprs))))
  10458. new-clauses)))
  10459. (define (eqv-code-constant? x)
  10460. (or (number? x)
  10461. (char? x)
  10462. (and (pair? x)
  10463. (eq? (car x) 'quote)
  10464. (or (symbol? (cadr x))
  10465. (and (not (pair? (cadr x)))
  10466. (eqv-code-constant? (cadr x)))))
  10467. (memq x '(#t #f () #<unspecified> #<undefined> #<eof>))))
  10468. (define (cond-eqv? clause eqv-select or-ok)
  10469. (if (not (pair? clause))
  10470. (memq clause '(else #t))
  10471. ;; it's eqv-able either directly or via memq/memv, or via (or ... eqv-able clauses)
  10472. ;; all clauses involve the same (eventual case) selector
  10473. (case (car clause)
  10474. ((eq? eqv? = equal? char=? char-ci=?)
  10475. (if (eqv-code-constant? (cadr clause))
  10476. (equal? eqv-select (caddr clause))
  10477. (and (eqv-code-constant? (caddr clause))
  10478. (equal? eqv-select (cadr clause)))))
  10479. ((memq memv member)
  10480. (and (equal? eqv-select (cadr clause))
  10481. (pair? (caddr clause))
  10482. (eq? (caaddr clause) 'quote)
  10483. (or (not (eq? (car clause) 'member))
  10484. (every? (lambda (x)
  10485. (or (number? x)
  10486. (char? x)
  10487. (symbol? x)
  10488. (memq x '(#t #f () #<unspecified> #<undefined> #<eof>))))
  10489. (cdr (caddr clause))))))
  10490. ((or)
  10491. (and or-ok
  10492. (every? (lambda (p)
  10493. (cond-eqv? p eqv-select #f))
  10494. (cdr clause))))
  10495. ((not null? eof-object? zero? boolean?)
  10496. (equal? eqv-select (cadr clause)))
  10497. (else #f))))
  10498. (define (find-constant-exprs caller vars body)
  10499. (if (or (tree-set-member '(call/cc call-with-current-continuation lambda lambda* define define*
  10500. define-macro define-macro* define-bacro define-bacro* define-constant define-expansion)
  10501. body)
  10502. (let set-walk ((tree body)) ; generalized set! causes confusion
  10503. (and (pair? tree)
  10504. (or (and (eq? (car tree) 'set!)
  10505. (pair? (cdr tree))
  10506. (pair? (cadr tree)))
  10507. (set-walk (car tree))
  10508. (set-walk (cdr tree))))))
  10509. ()
  10510. (let ((refs (let ((vs (out-vars caller vars body)))
  10511. (remove-if (lambda (v)
  10512. (or (assq v vars) ; vars = do-loop steppers
  10513. (memq v (cadr vs)))) ; (cadr vs) = sets
  10514. (car vs))))
  10515. ;; refs are the external variables accessed in the do-loop body
  10516. ;; that are not set or shadowed or changed (vector-set! etc)
  10517. (constant-exprs ()))
  10518. (let expr-walk ((tree body))
  10519. (when (pair? tree)
  10520. (if (let all-ok? ((tree tree))
  10521. (if (symbol? tree)
  10522. (memq tree refs)
  10523. (or (not (pair? tree))
  10524. (eq? (car tree) 'quote)
  10525. (and (hash-table-ref no-side-effect-functions (car tree))
  10526. (or (not (hash-table-ref syntaces (car tree)))
  10527. (memq (car tree) '(if begin cond or and unless when)))
  10528. (not (hash-table-ref makers (car tree)))
  10529. (list? (cdr tree))
  10530. (every? all-ok? (cdr tree))))))
  10531. (if (not (or (eq? (car tree) 'quote) (member tree constant-exprs)))
  10532. (set! constant-exprs (cons tree constant-exprs)))
  10533. (begin
  10534. (if (pair? (car tree))
  10535. (expr-walk (car tree)))
  10536. (when (pair? (cdr tree))
  10537. (let ((f (cdr tree)))
  10538. (case (car f)
  10539. ((case)
  10540. (when (and (pair? (cdr f))
  10541. (pair? (cddr f)))
  10542. (expr-walk (cadr f))
  10543. (for-each (lambda (c)
  10544. (expr-walk (cdr c)))
  10545. (cddr f))))
  10546. ((letrec letrec*)
  10547. (when (pair? (cddr f))
  10548. (for-each (lambda (c)
  10549. (if (and (pair? c)
  10550. (pair? (cdr c)))
  10551. (expr-walk (cadr c))))
  10552. (cadr f))
  10553. (expr-walk (cddr f))))
  10554. ((let let*)
  10555. (when (pair? (cddr f))
  10556. (if (symbol? (cadr f))
  10557. (set! f (cdr f)))
  10558. (for-each (lambda (c)
  10559. (if (and (pair? c)
  10560. (pair? (cdr c)))
  10561. (expr-walk (cadr c))))
  10562. (cadr f))
  10563. (expr-walk (cddr f))))
  10564. ((do)
  10565. (when (and (list? (cadr f))
  10566. (list? (cddr f))
  10567. (pair? (cdddr f)))
  10568. (for-each (lambda (c)
  10569. (if (pair? (cddr c))
  10570. (expr-walk (caddr c))))
  10571. (cadr f))
  10572. (expr-walk (cdddr f))))
  10573. (else (for-each expr-walk f)))))))))
  10574. (when (pair? constant-exprs)
  10575. (set! constant-exprs (remove-if (lambda (p)
  10576. (or (null? (cdr p))
  10577. (and (null? (cddr p))
  10578. (memq (car p) '(not -))
  10579. (symbol? (cadr p)))
  10580. (tree-unquoted-member 'port-line-number p)))
  10581. constant-exprs)))
  10582. constant-exprs)))
  10583. (define (partition-form start len)
  10584. (let ((ps (make-vector len))
  10585. (qs (make-vector len)))
  10586. (do ((i 0 (+ i 1))
  10587. (p start (cdr p)))
  10588. ((= i len))
  10589. (set! (ps i) (cadar p))
  10590. (set! (qs i) (reverse (cadar p))))
  10591. (let ((header-len (length (ps 0))))
  10592. (let ((trailer-len header-len)
  10593. (result-min-len header-len))
  10594. (do ((i 1 (+ i 1)))
  10595. ((= i len))
  10596. (set! result-min-len (min result-min-len (length (ps i))))
  10597. (do ((k 1 (+ k 1))
  10598. (p (cdr (ps i)) (cdr p))
  10599. (f (cdr (ps 0)) (cdr f)))
  10600. ((or (= k header-len)
  10601. (not (pair? p))
  10602. (not (equal? (car p) (car f))))
  10603. (set! header-len k)))
  10604. (do ((k 0 (+ k 1))
  10605. (q (qs i) (cdr q))
  10606. (f (qs 0) (cdr f)))
  10607. ((or (= k trailer-len)
  10608. (not (pair? q))
  10609. (not (equal? (car q) (car f))))
  10610. (set! trailer-len k))))
  10611. (if (= result-min-len header-len)
  10612. (begin
  10613. (set! header-len (- header-len 1))
  10614. (set! trailer-len 0)))
  10615. (if (<= result-min-len (+ header-len trailer-len))
  10616. (set! trailer-len (- result-min-len header-len 1)))
  10617. (values header-len trailer-len result-min-len)))))
  10618. (define (one-call-and-dots body) ; body is unchanged here, so it's not interesting
  10619. (if (< (tree-leaves body) 30)
  10620. (if (null? (cdr body))
  10621. body
  10622. (list (car body) '...))
  10623. (if (pair? (car body))
  10624. (list (list (caar body) '...))
  10625. (list (car body) '...))))
  10626. (define (replace-redundant-named-let caller form outer-name outer-args inner)
  10627. (when (proper-list? outer-args) ; can be null
  10628. (let ((inner-name (cadr inner))
  10629. (inner-args (caddr inner))
  10630. (inner-body (cdddr inner)))
  10631. (do ((p outer-args (cdr p))
  10632. (a inner-args (cdr a)))
  10633. ((or (null? p)
  10634. (not (pair? a))
  10635. (not (pair? (car a)))
  10636. (and (not (eq? (car p) (caar a)))
  10637. (tree-memq (car p) inner-body)))
  10638. ;; args can be reversed, but rarely match as symbols
  10639. (when (and (null? p)
  10640. (or (null? a)
  10641. (and (null? (cdr a))
  10642. (code-constant? (cadar a)))))
  10643. (let* ((args-match (do ((p outer-args (cdr p))
  10644. (a inner-args (cdr a)))
  10645. ((or (null? p)
  10646. (not (eq? (car p) (caar a)))
  10647. (not (eq? (caar a) (cadar a))))
  10648. (null? p))))
  10649. (args-aligned (and (not args-match)
  10650. (do ((p outer-args (cdr p))
  10651. (a inner-args (cdr a)))
  10652. ((or (null? p)
  10653. (not (eq? (car p) (cadar a))))
  10654. (null? p))))))
  10655. (when (or args-match args-aligned)
  10656. (let ((definer (if (null? a) 'define 'define*))
  10657. (extras (if (and (pair? a)
  10658. (quoted-null? (cadar a)))
  10659. (list (list (caar a) ()))
  10660. a)))
  10661. ;; (define (f61 x) (let loop ((y x)) (if (positive? y) (loop (- y 1)) 0))) -> (define (f61 y) (if (positive? y) (f61 (- y 1)) 0))
  10662. (lint-format "~A ~A" caller
  10663. (if (null? a) "perhaps" "a toss-up -- perhaps")
  10664. (lists->string form
  10665. `(,definer (,outer-name
  10666. ,@(if args-match
  10667. outer-args
  10668. (do ((result ())
  10669. (p outer-args (cdr p))
  10670. (a inner-args (cdr a)))
  10671. ((null? p)
  10672. (reverse result))
  10673. (set! result (cons (caar a) result))))
  10674. ,@extras)
  10675. ,@(tree-subst outer-name inner-name inner-body)))))))))))))
  10676. (define (set!? form env)
  10677. (and *report-any-!-as-setter* ; (inc! x) when inc! is unknown, assume it sets x
  10678. (symbol? (car form))
  10679. (pair? (cdr form))
  10680. (or (symbol? (cadr form))
  10681. (and (pair? (cddr form))
  10682. (symbol? (caddr form))))
  10683. (not (var-member (car form) env))
  10684. (not (hash-table-ref built-in-functions (car form)))
  10685. (let ((str (symbol->string (car form))))
  10686. (char=? (string-ref str (- (length str) 1)) #\!))))
  10687. (define (set-target name form env)
  10688. (and (pair? form)
  10689. (or (and (pair? (cdr form))
  10690. (or (eq? (cadr form) name) ; (pop! x)
  10691. (and (pair? (cddr form)) ; (push! y x)
  10692. (eq? (caddr form) name)))
  10693. (or (eq? (car form) 'set!) ; (set! x y)
  10694. (set!? form env)))
  10695. (set-target name (car form) env)
  10696. (set-target name (cdr form) env))))
  10697. (define (check-definee caller sym form env)
  10698. (cond ((keyword? sym) ; (define :x 1)
  10699. (lint-format "keywords are constants ~A" caller sym))
  10700. ((and (eq? sym 'pi) ; (define pi (atan 0 -1))
  10701. (member (caddr form) '((atan 0 -1)
  10702. (acos -1)
  10703. (* 2 (acos 0))
  10704. (* 4 (atan 1))
  10705. (* 4 (atan 1 1)))))
  10706. (lint-format "~A is one of its many names, but pi is a predefined constant in s7" caller (caddr form)))
  10707. ((constant? sym) ; (define most-positive-fixnum 432)
  10708. (lint-format "~A is a constant in s7: ~A" caller sym form))
  10709. ((eq? sym 'quote)
  10710. (lint-format "either a stray quote, or a real bad idea: ~A" caller (truncated-list->string form)))
  10711. ((pair? sym)
  10712. (check-definee caller (car sym) form env))
  10713. ((let ((v (var-member sym env)))
  10714. (and (var? v)
  10715. (eq? (var-definer v) 'define-constant)
  10716. (not (equal? (caddr form) (var-initial-value v)))))
  10717. => (lambda (v)
  10718. (let ((line (if (and (pair? (var-initial-value v))
  10719. (positive? (pair-line-number (var-initial-value v))))
  10720. (format #f "(line ~D): " (pair-line-number (var-initial-value v)))
  10721. "")))
  10722. (lint-format "~A in ~A is already a constant, defined ~A~A" caller sym
  10723. (truncated-list->string form)
  10724. line
  10725. (truncated-list->string (var-initial-value v))))))))
  10726. (define binders (let ((h (make-hash-table)))
  10727. (for-each
  10728. (lambda (op)
  10729. (set! (h op) #t))
  10730. '(let let* letrec letrec* do
  10731. lambda lambda* define define*
  10732. call/cc call-with-current-continuation
  10733. define-macro define-macro* define-bacro define-bacro* define-constant define-expansion
  10734. load eval eval-string require))
  10735. h))
  10736. (define lint-let-reduction-factor 3) ; maybe make this a global switch -- the higher this number, the fewer let-reduction suggestions
  10737. (define walker-functions
  10738. (let ((h (make-hash-table)))
  10739. ;; ---------------- define ----------------
  10740. (let ()
  10741. (define (define-walker caller form env)
  10742. (if (< (length form) 2)
  10743. (begin
  10744. (lint-format "~S makes no sense" caller form)
  10745. env)
  10746. (let ((sym (cadr form))
  10747. (val (cddr form))
  10748. (head (car form)))
  10749. (if (symbol? sym)
  10750. (begin
  10751. (check-definee caller sym form env)
  10752. (if (memq head '(define define-constant define-envelope
  10753. define-public define*-public defmacro-public define-inlinable
  10754. define-integrable define^))
  10755. (let ((len (length form)))
  10756. (if (not (= len 3)) ; (define a b c)
  10757. (lint-format "~A has ~A value~A?"
  10758. caller (truncated-list->string form)
  10759. (if (< len 3)
  10760. (values "no" "")
  10761. (values "too many" "s")))))
  10762. (lint-format "~A is messed up" caller (truncated-list->string form)))
  10763. (if (not (pair? val))
  10764. env
  10765. (begin
  10766. (if (and (null? (cdr val))
  10767. (equal? sym (car val))) ; (define a a)
  10768. (lint-format "this ~A is either not needed, or is an error: ~A" caller head (truncated-list->string form)))
  10769. (if (not (pair? (car val)))
  10770. (begin
  10771. (cond ((and (not (memq caller '(module cond-expand)))
  10772. (hash-table-ref other-identifiers sym))
  10773. => (lambda (p)
  10774. (lint-format "~A is used before it is defined: ~A" caller sym form))))
  10775. (cons (make-var :name sym :initial-value (car val) :definer head) env))
  10776. (let ((e (lint-walk (if (and (pair? (car val))
  10777. (eq? (caar val) 'letrec))
  10778. 'define sym)
  10779. (car val) env)))
  10780. (if (or (not (pair? e))
  10781. (eq? e env)
  10782. (not (memq (var-name (car e)) '(:lambda :dilambda)))) ; (define x (lambda ...))
  10783. (cons (make-var :name sym :initial-value (car val) :definer head) env)
  10784. (begin
  10785. (set! (var-name (car e)) sym)
  10786. (let ((val (caddr form)))
  10787. (when (and (eq? (car val) 'lambda) ; (define sym (lambda args (let name...))), let here happens rarely
  10788. (proper-list? (cadr val))
  10789. (pair? (caddr val))
  10790. (null? (cdddr val))
  10791. (eq? (caaddr val) 'let)
  10792. (symbol? (cadr (caddr val))))
  10793. (replace-redundant-named-let caller form sym (cadr val) (caddr val))))
  10794. ;; (define x (letrec ((y (lambda...))) (lambda (...) (y...)))) -> (define (x...)...)
  10795. (let* ((let-form (cdaddr form))
  10796. (var (and (pair? (car let-form))
  10797. (null? (cdar let-form)) ; just one var in let/rec
  10798. (caar let-form))))
  10799. ;; let-form here can be cdr of (lambda...) or (let|letrec ... lambda)
  10800. (when (and (pair? var)
  10801. (symbol? (car var))
  10802. (pair? (cdr let-form))
  10803. (pair? (cadr let-form))
  10804. (null? (cddr let-form)) ; just one form in the let/rec
  10805. (pair? (cdr var))
  10806. (pair? (cadr var))
  10807. (pair? (cdadr var))
  10808. (eq? (caadr var) 'lambda) ; var is lambda
  10809. (proper-list? (cadadr var))) ; it has no rest arg
  10810. (let ((body (cadr let-form)))
  10811. (when (and (eq? (car body) 'lambda) ; let/rec body is lambda calling var
  10812. (proper-list? (cadr body)) ; rest args are a headache
  10813. (pair? (caddr body))) ; (lambda (...) (...) where car is letrec func name
  10814. (if (eq? (caaddr body) (car var))
  10815. (lint-format "perhaps ~A" caller
  10816. (lists->string form
  10817. `(define (,sym ,@(cadr body))
  10818. (let ,(car var)
  10819. ,(map list (cadadr var) (cdaddr body))
  10820. ,@(cddadr var)))))
  10821. (let ((call (find-call (car var) (caddr body))))
  10822. (when (and (pair? call) ; inner lambda body is (...some-expr...(sym...) ...)
  10823. (= (tree-count1 (car var) (caddr body) 0) 1))
  10824. (let ((new-call `(let ,(car var)
  10825. ,(map list (cadadr var) (cdr call))
  10826. ,@(cddadr var))))
  10827. (lint-format "perhaps ~A" caller
  10828. (lists->string form
  10829. `(define (,sym ,@(cadr body))
  10830. ,(tree-subst new-call call
  10831. (caddr body)))))))))))))
  10832. e))))))) ; symbol? sym
  10833. ;; not (symbol? sym)
  10834. (if (and (pair? sym) ; cadr form
  10835. (pair? val) ; cddr form
  10836. (not (pair? (car sym)))) ; pair would indicate a curried func or something equally stupid
  10837. (let ((outer-args (cdr sym))
  10838. (outer-name (car sym)))
  10839. (cond ((not *report-forward-functions*))
  10840. ;; need to ignore macro usages here -- this happens ca 20000 times!
  10841. ((hash-table-ref other-identifiers (car sym))
  10842. => (lambda (p)
  10843. (lint-format "~A is used before it is defined" caller (car sym)))))
  10844. (if (and *report-boolean-functions-misbehaving*
  10845. (symbol? (car sym))
  10846. (not (memq head '(lambda lambda*))) ; how to catch this case? -- this appears to be ignored
  10847. (char=? #\? ((reverse (symbol->string (car sym))) 0)))
  10848. (catch 'one-is-enough
  10849. (lambda ()
  10850. (return-walker (list-ref val (- (length val) 1))
  10851. (lambda (last)
  10852. (when (or (and (code-constant? last)
  10853. (not (boolean? last))
  10854. (not (and (pair? last)
  10855. (eq? (car last) 'quote)
  10856. (boolean? (cadr last)))))
  10857. (and (pair? last)
  10858. (let ((sig (arg-signature (car last) env)))
  10859. (and (pair? sig)
  10860. (if (pair? (car sig))
  10861. (not (tree-set-member '(boolean? #t values) (car sig)))
  10862. (not (memq (car sig) '(boolean? #t values))))))))
  10863. (lint-format "~A looks boolean, but it can return ~A" caller (car sym) (truncated-list->string last))
  10864. (throw 'one-is-enough)))))
  10865. (lambda args #f)))
  10866. (check-definee caller (car sym) form env)
  10867. (when (pair? (car val))
  10868. (when (eq? (caar val) 'let)
  10869. (when (pair? (cadar val))
  10870. (do ((inner-vars (cadar val))
  10871. (p outer-args (cdr p)))
  10872. ((not (pair? p)))
  10873. (cond ((assq (car p) inner-vars) =>
  10874. (lambda (v)
  10875. (if (eq? (cadr v) (car p))
  10876. ;; (define (f70 a b) (let ((a a) (b b)) (+ a b)))
  10877. (lint-format "in ~A this let binding is pointless: ~A" caller
  10878. (truncated-list->string form)
  10879. v)))))))
  10880. ;; define + redundant named-let -- sometimes rewrites to define*
  10881. (when (and (symbol? (cadar val))
  10882. (null? (cdr val)))
  10883. (replace-redundant-named-let caller form outer-name outer-args (car val))))
  10884. ;; perhaps this block should be on a *report-* switch --
  10885. ;; it translates some internal defines into named lets
  10886. ;; (or just normal lets, etc)
  10887. ;; this is not redundant given the walk-body translations because here
  10888. ;; we have the outer parameters and can check those against the inner ones
  10889. ;; leading (sometimes) to much nicer rewrites.
  10890. (when (and (eq? (caar val) 'define) ; define* does not happen here
  10891. (pair? (cdr val))
  10892. (pair? (cadar val))) ; inner define (name ...)
  10893. (let ((inner-name (caadar val))
  10894. (inner-args (cdadar val))
  10895. (inner-body (cddar val))
  10896. (outer-body (cdddr form)))
  10897. (when (and (symbol? inner-name)
  10898. (proper-list? inner-args)
  10899. (pair? (car outer-body))
  10900. (= (tree-count1 inner-name outer-body 0) 1))
  10901. (let ((call (find-call inner-name outer-body)))
  10902. (when (pair? call)
  10903. (set! last-rewritten-internal-define (car val))
  10904. (let ((new-call (if (tree-memq inner-name inner-body)
  10905. (if (and (null? inner-args)
  10906. (null? outer-args))
  10907. (if (null? (cdr inner-body))
  10908. (car (tree-subst outer-name inner-name inner-body))
  10909. `(begin ,@(tree-subst outer-name inner-name inner-body)))
  10910. `(let ,inner-name
  10911. ,(if (null? inner-args) () (map list inner-args (cdr call)))
  10912. ,@inner-body))
  10913. (if (or (null? inner-args)
  10914. (and (equal? inner-args outer-args)
  10915. (equal? inner-args (cdr call))))
  10916. (if (null? (cdr inner-body))
  10917. (car (tree-subst outer-name inner-name inner-body))
  10918. `(begin ,@(tree-subst outer-name inner-name inner-body)))
  10919. `(let ,(map list inner-args (cdr call))
  10920. ,@inner-body)))))
  10921. ;; (define (f11 a b) (define (f12 a b) (if (positive? a) (+ a b) b)) (f12 a b)) ->
  10922. ;; (define (f11 a b) (if (positive? a) (+ a b) b))
  10923. (lint-format "perhaps ~A" caller
  10924. (lists->string form
  10925. `(,head ,sym
  10926. ,@(let ((p (tree-subst new-call call outer-body)))
  10927. (if (and (pair? p)
  10928. (pair? (car p))
  10929. (eq? (caar p) 'begin))
  10930. (cdar p)
  10931. p))))))))))))
  10932. (when (pair? outer-args)
  10933. (if (repeated-member? (proper-list outer-args) env)
  10934. (lint-format "~A parameter is repeated: ~A" caller head (truncated-list->string sym)))
  10935. (cond ((memq head '(define* define-macro* define-bacro* define*-public))
  10936. (check-star-parameters outer-name outer-args env))
  10937. ((list-any? keyword? outer-args)
  10938. (lint-format "~A parameter can't be a keyword: ~A" caller outer-name sym))
  10939. ((memq 'pi outer-args)
  10940. (lint-format "~A parameter can't be a constant: ~A" caller outer-name sym)))
  10941. ;; look for built-in names used as parameter names and used as functions internally(!)
  10942. ;; this requires a tree walker to ignore (for example) (let loop ((string string))...)
  10943. (for-each (lambda (p)
  10944. (let ((par (if (pair? p) (car p) p)))
  10945. (when (or (hash-table-ref built-in-functions par)
  10946. (hash-table-ref syntaces par))
  10947. (let ((call (call-with-exit
  10948. (lambda (return)
  10949. (let loop ((tree (cddr form)))
  10950. (if (pair? tree)
  10951. (if (eq? (car tree) par)
  10952. (return tree)
  10953. (case (car tree)
  10954. ((quote) #f)
  10955. ((let let*)
  10956. (if (pair? (cdr tree))
  10957. (if (symbol? (cadr tree))
  10958. (if (not (tree-memq par (caddr tree)))
  10959. (loop (cdddr tree)))
  10960. (if (not (tree-memq par (cadr tree)))
  10961. (loop (cddr tree))))))
  10962. ((letrec letrec*)
  10963. (if (and (pair? (cdr tree))
  10964. (not (tree-memq par (cadr tree))))
  10965. (loop (cddr tree))))
  10966. ((do)
  10967. (if (and (pair? (cdr tree))
  10968. (pair? (cddr tree))
  10969. (not (tree-memq par (cadr tree))))
  10970. (loop (cdddr tree))))
  10971. (else
  10972. (if (pair? (cdr tree))
  10973. (for-each loop (cdr tree)))
  10974. (if (pair? (car tree))
  10975. (loop (car tree))))))))))))
  10976. (if (and (pair? call)
  10977. (pair? (cdr call))
  10978. (not (eq? par (cadr call))))
  10979. (lint-format* caller ; (define (f50 abs) (abs -1))
  10980. (string-append (object->string outer-name) "'s parameter " (symbol->string par))
  10981. (string-append " is called " (truncated-list->string call))
  10982. ": find a less confusing parameter name!"))))))
  10983. outer-args))
  10984. (when (and (eq? head 'define-macro)
  10985. (pair? val)
  10986. (null? (cdr val)))
  10987. (let ((body (car val)))
  10988. (if (and (null? outer-args) ; look for C macros translated as define-macro! -- this happens a lot sad to say
  10989. (or (not (symbol? body))
  10990. (keyword? body))
  10991. (or (not (pair? body))
  10992. (and (eq? (car body) 'quote)
  10993. (not (symbol? (cadr body)))
  10994. (or (not (pair? (cadr body)))
  10995. (eq? (caadr body) 'quote)))
  10996. (not (or (memq (car body) '(quote quasiquote list cons append))
  10997. (tree-set-member '(#_{list} #_{apply_values} #_{append}) body)))))
  10998. (lint-format "perhaps ~A or ~A" caller
  10999. (lists->string form `(define ,outer-name ,(unquoted (car val))))
  11000. (truncated-list->string `(define (,outer-name) ,(unquoted (car val))))))
  11001. (when (pair? body)
  11002. (case (car body)
  11003. ((#_{list})
  11004. (when (and (quoted-symbol? (cadr body))
  11005. (proper-list? outer-args))
  11006. (if (and (equal? (cddr body) outer-args)
  11007. (or (not (hash-table-ref syntaces (cadadr body))) ; (define-macro (x y) `(lambda () ,y))
  11008. (memq (cadadr body) '(set! define))))
  11009. (lint-format "perhaps ~A" caller
  11010. (lists->string form `(define ,outer-name ,(cadadr body))))
  11011. (if (and (not (hash-table-ref syntaces (cadadr body)))
  11012. (not (any-macro? (cadadr body) env))
  11013. (every? (lambda (a)
  11014. (or (code-constant? a)
  11015. (and (memq a outer-args)
  11016. (= (tree-count1 a (cddr body) 0) 1))))
  11017. (cddr body)))
  11018. ;; marginal -- there are many debatable cases here
  11019. (lint-format "perhaps ~A" caller
  11020. (lists->string form `(define (,outer-name ,@outer-args)
  11021. (,(cadadr body) ,@(map unquoted (cddr body)))))))))
  11022. (let ((pargs (args->proper-list outer-args)))
  11023. (for-each (lambda (p)
  11024. (if (and (pair? p)
  11025. (eq? (car p) 'quote)
  11026. (pair? (cdr p))
  11027. (pair? (cadr p))
  11028. (tree-set-member pargs (cadr p)))
  11029. (lint-format "missing comma? ~A" caller form)))
  11030. (cdr body))))
  11031. ((quote)
  11032. ;; extra comma (unquote) is already caught elsewhere
  11033. (if (and (pair? (cdr body))
  11034. (pair? (cadr body))
  11035. (tree-set-member (args->proper-list outer-args) (cadr body)))
  11036. (lint-format "missing comma? ~A" caller form)))))))
  11037. (if (and (eq? head 'definstrument)
  11038. (string? (car val)))
  11039. (set! val (cdr val)))
  11040. (if (keyword? outer-name)
  11041. env
  11042. (lint-walk-function head outer-name outer-args val form env)))
  11043. (begin ; not (and (pair? sym)...)
  11044. (lint-format "strange form: ~A" head (truncated-list->string form))
  11045. (when (and (pair? sym)
  11046. (pair? (car sym)))
  11047. (let ((outer-args (cdr sym))
  11048. (outer-name (if (eq? head 'define*) (remove :optional (car sym)) (car sym))))
  11049. (if (symbol? (car outer-name))
  11050. ;; perhaps a curried definition -- as a public service, we'll rewrite the dumb thing
  11051. (begin
  11052. (lint-format "perhaps ~A" caller
  11053. (lists->string form `(,head ,outer-name
  11054. (lambda ,outer-args
  11055. ,@(cddr form)))))
  11056. (lint-walk-function head (car outer-name) (cdr outer-name) val form env)) ;val=(cddr form) I think
  11057. (when (pair? (car outer-name))
  11058. (if (symbol? (caar outer-name))
  11059. (begin
  11060. (lint-format "perhaps ~A" caller
  11061. (lists->string form `(,head ,(car outer-name)
  11062. (lambda ,(cdr outer-name)
  11063. (lambda ,outer-args
  11064. ,@(cddr form))))))
  11065. (lint-walk-function head (caar outer-name) (cdar outer-name) val form env))
  11066. (when (and (pair? (caar outer-name))
  11067. (symbol? (caaar outer-name)))
  11068. (lint-format "perhaps ~A" caller
  11069. (lists->string form `(,head ,(caar outer-name)
  11070. (lambda ,(cdar outer-name)
  11071. (lambda ,(cdr outer-name)
  11072. (lambda ,outer-args
  11073. ,@(cddr form)))))))
  11074. (lint-walk-function head (caaar outer-name) (cdaar outer-name) val form env)))))))
  11075. env))))))
  11076. (for-each (lambda (op)
  11077. (hash-table-set! h op define-walker))
  11078. '(define define* define-constant
  11079. define-macro define-macro* define-bacro define-bacro* define-expansion
  11080. definstrument defanimal define-envelope ; for clm
  11081. define-public define*-public defmacro-public define-inlinable
  11082. define-integrable define^))) ; these give more informative names in Guile and scmutils (MIT-scheme))
  11083. ;; ---------------- dilambda ----------------
  11084. (let ()
  11085. (define (dilambda-walker caller form env)
  11086. ;(format *stderr* "~A~%" form)
  11087. (let ((len (length form)))
  11088. (if (not (= len 3))
  11089. (begin
  11090. (lint-format "dilambda takes two arguments: ~A" caller (truncated-list->string form))
  11091. env)
  11092. (let ((getter (cadr form))
  11093. (setter (caddr form)))
  11094. (check-call caller 'dilambda form env)
  11095. (lint-walk caller setter env)
  11096. (let ((e (lint-walk caller getter env))) ; goes to lint-walk-function -> :lambda as first in e
  11097. (if (and (pair? e)
  11098. (eq? (var-name (car e)) :lambda))
  11099. (set! (var-name (car e)) :dilambda))
  11100. e)))))
  11101. (hash-table-set! h 'dilambda dilambda-walker))
  11102. ;; ---------------- lambda ----------------
  11103. (let ()
  11104. (define (lambda-walker caller form env)
  11105. (let ((len (length form))
  11106. (head (car form)))
  11107. (if (< len 3)
  11108. (begin
  11109. (lint-format "~A is messed up in ~A" caller head (truncated-list->string form))
  11110. env)
  11111. (let ((args (cadr form)))
  11112. (when (list? args)
  11113. (let ((arglen (length args)))
  11114. (if (null? args)
  11115. (if (eq? head 'lambda*) ; (lambda* ()...) -> (lambda () ...)
  11116. (lint-format "lambda* could be lambda ~A" caller form))
  11117. (begin ; args is a pair ; (lambda (a a) ...)
  11118. (let ((val (caddr form)))
  11119. (if (and (pair? val)
  11120. (eq? (car val) 'let)
  11121. (pair? (cadr val)))
  11122. (do ((inner-vars (cadr val))
  11123. (p (cadr form) (cdr p)))
  11124. ((not (pair? p)))
  11125. (cond ((assq (car p) inner-vars) =>
  11126. (lambda (v)
  11127. (if (eq? (cadr v) (car p))
  11128. (lint-format "in ~A this let binding is pointless: ~A" caller
  11129. (truncated-list->string form)
  11130. v))))))))
  11131. (if (repeated-member? (proper-list args) env)
  11132. (lint-format "~A parameter is repeated: ~A" caller head (truncated-list->string args)))
  11133. (if (eq? head 'lambda*) ; (lambda* (a :b) ...)
  11134. (check-star-parameters head args env)
  11135. (if (list-any? keyword? args) ; (lambda (:key) ...)
  11136. (lint-format "lambda arglist can't handle keywords (use lambda*)" caller)))))
  11137. (when (and (eq? head 'lambda) ; (lambda () (f)) -> f, (lambda (a b) (f a b)) -> f
  11138. (not (eq? caller 'case-lambda))
  11139. (= len 3)
  11140. (>= arglen 0)) ; not a dotted list
  11141. (let ((body (caddr form)))
  11142. (cond ((not (and (pair? body)
  11143. (symbol? (car body))
  11144. (not (memq (car body) '(and or))))))
  11145. ((equal? args (cdr body))
  11146. ;; (lambda (a b) (> a b)) -> >
  11147. (lint-format "perhaps ~A" caller (lists->string form (car body))))
  11148. ((equal? (reverse args) (cdr body))
  11149. (let ((rf (hash-table-ref reversibles (car body))))
  11150. ;; (lambda (a b) (> b a)) -> <
  11151. (if rf (lint-format "perhaps ~A" caller (lists->string form rf)))))
  11152. ((and (= arglen 1)
  11153. (hash-table-ref combinable-cxrs (car body)))
  11154. ((lambda* (cr arg) ; lambda* not lambda because combine-cxrs might return just #f
  11155. (and cr
  11156. (< (length cr) 5)
  11157. (eq? (car args) arg)
  11158. ;; (lambda (x) (cdr (cdr (car x)))) -> cddar
  11159. (lint-format "perhaps ~A" caller
  11160. (lists->string form (symbol "c" cr "r")))))
  11161. (combine-cxrs body))))))))
  11162. (if (and (or (symbol? args) ; (lambda args (apply f args)) -> f
  11163. (and (pair? args) ; (lambda #\a ...) !
  11164. (negative? (length args))))
  11165. (eq? head 'lambda)
  11166. (not (eq? caller 'case-lambda))
  11167. (= len 3))
  11168. (let ((body (caddr form)))
  11169. (if (and (pair? body)
  11170. (eq? (car body) 'apply)
  11171. (pair? (cdr body))
  11172. (symbol? (cadr body))
  11173. (not (memq (cadr body) '(and or)))
  11174. (pair? (cddr body))
  11175. (or (eq? args (caddr body))
  11176. (and (pair? args)
  11177. (equal? (cddr body) (proper-list args)))))
  11178. ;; (lambda args (apply + args)) -> +
  11179. (lint-format "perhaps ~A" caller (lists->string form (cadr body))))))
  11180. (lint-walk-function head caller args (cddr form) form env)
  11181. ;; not env as return value here -- return the lambda+old env via lint-walk-function
  11182. ))))
  11183. (hash-table-set! h 'lambda lambda-walker)
  11184. (hash-table-set! h 'lambda* lambda-walker))
  11185. ;; ---------------- set! ----------------
  11186. (let ()
  11187. (define (set-walker caller form env)
  11188. (if (not (= (length form) 3))
  11189. (begin
  11190. (lint-format "set! has too ~A arguments: ~S" caller (if (> (length form) 3) "many" "few") form)
  11191. env)
  11192. (let ((settee (cadr form))
  11193. (setval (caddr form)))
  11194. (if (symbol? setval)
  11195. (set-ref setval caller form env))
  11196. (let ((result (lint-walk caller setval env)))
  11197. (if (symbol? settee)
  11198. (if (constant? settee) ; (set! pi 3)
  11199. (lint-format "can't set! ~A (it is a constant)" caller (truncated-list->string form))
  11200. (let ((v (var-member settee env)))
  11201. (if (and (var? v)
  11202. (eq? (var-definer v) 'define-constant))
  11203. (let ((line (if (and (pair? (var-initial-value v))
  11204. (positive? (pair-line-number (var-initial-value v))))
  11205. (format #f "(line ~D): " (pair-line-number (var-initial-value v)))
  11206. "")))
  11207. (lint-format "can't set! ~A in ~A (it is a constant: ~A~A)" caller settee
  11208. (truncated-list->string form)
  11209. line
  11210. (truncated-list->string (var-initial-value v)))))))
  11211. (if (not (pair? settee)) ; (set! 3 1)
  11212. (lint-format "can't set! ~A" caller (truncated-list->string form))
  11213. (begin
  11214. (if (memq (car settee) '(vector-ref list-ref string-ref hash-table-ref))
  11215. ;; (set! (vector-ref v 0) 3)
  11216. (lint-format "~A as target of set!~A" caller (car settee) (truncated-list->string form)))
  11217. (lint-walk caller settee env) ; this counts as a reference since it's by reference so to speak
  11218. ;; try type check (dilambda signatures)
  11219. (when (symbol? (car settee))
  11220. (let ((f (symbol->value (car settee) *e*)))
  11221. (when (dilambda? f)
  11222. (let ((sig (procedure-signature (procedure-setter f)))
  11223. (settee-len (length settee)))
  11224. (when (and (pair? sig)
  11225. (positive? settee-len)
  11226. (pair? (list-tail sig settee-len)))
  11227. (let ((checker (list-ref sig settee-len))
  11228. (arg-type (->lint-type setval)))
  11229. (when (and (symbol? checker)
  11230. (not (compatible? checker arg-type)))
  11231. ;; (set! (print-length) "asd")
  11232. (lint-format "~A: new value should be a~A ~A: ~S: ~A"
  11233. caller (car settee)
  11234. (if (char=? (string-ref (format #f "~A" checker) 0) #\i) "n" "")
  11235. checker arg-type
  11236. (truncated-list->string form)))))))))
  11237. (set! settee (do ((sym (car settee) (car sym)))
  11238. ((not (pair? sym)) sym))))))
  11239. (if (symbol? (cadr form)) ; see do directly above -- sets settee so we have to go back to (cadr form)
  11240. (set-set (cadr form) caller form env)
  11241. (if (and (pair? (cadr form))
  11242. (symbol? settee))
  11243. (set-ref settee caller `(implicit-set ,@(cdr form)) env)))
  11244. (if (equal? (cadr form) setval) ; not settee here! ; (set! a a)
  11245. (lint-format "pointless set! ~A" caller (truncated-list->string form)))
  11246. (when (and (pair? setval)
  11247. (symbol? settee))
  11248. (case (car setval)
  11249. ((if) ; (set! x (if y x 1)) -> (if (not y) (set! x 1))
  11250. (if (= (length setval) 4)
  11251. (if (eq? settee (caddr setval))
  11252. (lint-format "perhaps ~A" caller
  11253. (lists->string form `(if (not ,(cadr setval)) (set! ,settee ,(cadddr setval)))))
  11254. (if (eq? settee (cadddr setval))
  11255. (lint-format "perhaps ~A" caller
  11256. (lists->string form `(if ,(cadr setval) (set! ,settee ,(caddr setval)))))))))
  11257. ((cond) ; (set! x (cond (z w) (else x))) -> (if z (set! x w)) -- this never happens
  11258. (if (and (= (length setval) 3)
  11259. (memq (caaddr setval) '(#t else))
  11260. (null? (cddr (caddr setval)))
  11261. (null? (cddadr setval)))
  11262. (if (eq? (cadr (caddr setval)) (cadr form))
  11263. (lint-format "perhaps ~A" caller
  11264. (lists->string form `(if ,(caadr setval) (set! ,(cadr form) ,(cadadr setval)))))
  11265. (if (eq? (cadadr setval) (cadr form))
  11266. (lint-format "perhaps ~A" caller
  11267. (lists->string form `(if (not ,(caadr setval)) (set! ,(cadr form) ,(cadr (caddr setval))))))))))
  11268. ((or) ; (set! x (or x y)) -> (if (not x) (set! x y))
  11269. (if (and (= (length setval) 3) ; the other case here is not improved by using 'if
  11270. (eq? settee (cadr setval)))
  11271. (lint-format "perhaps ~A" caller
  11272. (lists->string form `(if (not ,settee) (set! ,settee ,(caddr setval)))))))
  11273. ((and)
  11274. (if (= (length setval) 3) ; (set! x (and x y)) -> (if x (set! x y))
  11275. (if (eq? settee (cadr setval))
  11276. (lint-format "perhaps ~A" caller
  11277. (lists->string form `(if ,settee (set! ,settee ,(caddr setval)))))
  11278. (if (eq? settee (caddr setval))
  11279. (lint-format "perhaps ~A" caller
  11280. (lists->string form `(if (not ,(cadr setval)) (set! ,settee #f))))))))))
  11281. result))))
  11282. (hash-table-set! h 'set! set-walker))
  11283. ;; ---------------- quote ----------------
  11284. (let ()
  11285. (define (quote-walker caller form env)
  11286. (let ((len (length form)))
  11287. (if (negative? len)
  11288. (lint-format "stray dot in quote's arguments? ~S" caller form)
  11289. (if (not (= len 2))
  11290. (lint-format "quote has too ~A arguments: ~S" caller (if (> len 2) "many" "few") form)
  11291. (let ((arg (cadr form)))
  11292. (if (pair? arg)
  11293. (if (> (length arg) 8)
  11294. (hash-table-set! big-constants arg (+ 1 (or (hash-table-ref big-constants arg) 0))))
  11295. (unless (or (>= quote-warnings 20)
  11296. (and (symbol? arg)
  11297. (not (keyword? arg))))
  11298. (set! quote-warnings (+ quote-warnings 1)) ; (char? '#\a)
  11299. (lint-format "quote is not needed here: ~A~A" caller ; this is by far the most common message from lint
  11300. (truncated-list->string form)
  11301. (if (= quote-warnings 20) "; will ignore this error henceforth." ""))))))))
  11302. env)
  11303. (hash-table-set! h 'quote quote-walker))
  11304. ;; ---------------- if ----------------
  11305. (let ()
  11306. (define definers (let ((h (make-hash-table)))
  11307. (for-each (lambda (d)
  11308. (hash-table-set! h d #t))
  11309. '(define define* define-constant lambda lambda* curlet require load eval eval-string
  11310. define-macro define-macro* define-bacro define-bacro* define-expansion
  11311. definstrument defanimal define-envelope
  11312. define-values define-module define-method
  11313. define-syntax define-public define-inlinable define-integrable define^))
  11314. h))
  11315. (define (if-walker caller form env)
  11316. (let ((len (length form)))
  11317. (if (> len 4)
  11318. (lint-format "if has too many clauses: ~A" caller (truncated-list->string form))
  11319. (if (< len 3)
  11320. (lint-format "if has too few clauses: ~A" caller (truncated-list->string form))
  11321. (let ((test (cadr form))
  11322. (true (caddr form))
  11323. (false (if (= len 4) (cadddr form) 'no-false))
  11324. (expr (simplify-boolean (cadr form) () () env))
  11325. (suggestion made-suggestion)
  11326. (true-op (and (pair? (caddr form)) (caaddr form)))
  11327. (true-rest (and (pair? (caddr form)) (cdaddr form)))
  11328. (false-op (and (= len 4) (pair? (cadddr form)) (car (cadddr form))))
  11329. (false-rest (and (= len 4) (pair? (cadddr form)) (cdr (cadddr form)))))
  11330. (if (eq? false #<unspecified>)
  11331. (lint-format "this #<unspecified> is redundant: ~A" caller form))
  11332. (if (and (symbol? test)
  11333. (pair? true)
  11334. (memq test true))
  11335. (and-incomplete form 'if test true env)
  11336. (when (pair? test)
  11337. (if (and (eq? (car test) 'not)
  11338. (symbol? (cadr test))
  11339. (pair? false)
  11340. (memq (cadr test) false))
  11341. (and-incomplete form 'if2 (cadr test) false env))
  11342. (if (and (hash-table-ref bools (car test))
  11343. (pair? true))
  11344. (if (member (cadr test) true)
  11345. (and-forgetful form 'if test true env)
  11346. (do ((p true (cdr p)))
  11347. ((or (not (pair? p))
  11348. (and (pair? (car p))
  11349. (member (cadr test) (car p))))
  11350. (if (pair? p)
  11351. (and-forgetful form 'if test (car p) env)))))
  11352. (if (and (eq? (car test) 'not)
  11353. (pair? (cadr test))
  11354. (pair? false)
  11355. (hash-table-ref bools (caadr test)))
  11356. (if (member (cadadr test) false)
  11357. (and-forgetful form 'if2 (cadr test) false env)
  11358. (do ((p false (cdr p)))
  11359. ((or (not (pair? p))
  11360. (and (pair? (car p))
  11361. (member (cadadr test) (car p))))
  11362. (if (pair? p)
  11363. (and-forgetful form 'if2 (cadr test) (car p) env)))))))))
  11364. (when (and (pair? true)
  11365. (pair? false)
  11366. (not (memq true-op (list 'quote {list})))
  11367. (not (any-macro? true-op env))
  11368. (or (not (hash-table-ref syntaces true-op))
  11369. (memq true-op '(let let* set! and or begin)))
  11370. (pair? true-rest))
  11371. (define (tree-subst-eq new old tree)
  11372. ;; tree-subst above substitutes every occurence of 'old with 'new, so we check
  11373. ;; in advance that 'old only occurs once in the tree (via tree-count1). Here
  11374. ;; 'old may occur any number of times, but we want to change it only once,
  11375. ;; so we keep the actual pointer to it and use eq?. (This assumes no shared code?)
  11376. (cond ((eq? old tree)
  11377. (cons new (cdr tree)))
  11378. ((not (pair? tree))
  11379. tree)
  11380. ((eq? (car tree) 'quote)
  11381. (copy-tree tree))
  11382. (else (cons (tree-subst-eq new old (car tree))
  11383. (tree-subst-eq new old (cdr tree))))))
  11384. ;; maybe move the unless before this
  11385. (let ((diff (let differ-in-one ((p true)
  11386. (q false))
  11387. (and (pair? p)
  11388. (pair? q)
  11389. (if (equal? (car p) (car q))
  11390. (differ-in-one (cdr p) (cdr q))
  11391. (and (equal? (cdr p) (cdr q))
  11392. (or (and (pair? (car p))
  11393. (not (eq? (caar p) 'quote))
  11394. (pair? (car q))
  11395. (not (eq? (caar q) 'quote))
  11396. (differ-in-one (car p) (car q)))
  11397. (list p (list (car p) (car q))))))))))
  11398. (if (pair? diff)
  11399. (unless (or (and (equal? true-op (caadr diff)) ; (if x (+ y 1) (- y 1)) -- are we trying to keep really simple stuff out?
  11400. (or (hash-table-ref syntaces true-op)
  11401. (hash-table-ref syntaces false-op))
  11402. (any? pair? true-rest)) ; (if x (set! y (+ x 1)) (set! y 1))
  11403. (and (eq? true-op 'set!) ; (if x (set! y w) (set! z w))
  11404. (equal? (caar diff) (car true-rest))))
  11405. (let ((subst-loc (car diff)))
  11406. ;; for let/let* if tree-subst position can't affect the test, just subst, else save test first
  11407. ;; named let diff in args gets no hits
  11408. (if (memq true-op '(let let*))
  11409. (if (not (or (symbol? (car true-rest)) ; assume named let is moving an if outside the loop
  11410. (eq? subst-loc true-rest))) ; avoid confusion about the vars list
  11411. (let ((vars (car true-rest)))
  11412. ;; (if x (let ((y (abs x))) (display z) y) (let ((y (log x))) (display z) y)) -> (let ((y ((if x abs log) x))) (display z) y)
  11413. (lint-format "perhaps ~A" caller
  11414. (lists->string form
  11415. (if (and (pair? vars)
  11416. (case true-op
  11417. ((let) (tree-memq subst-loc vars))
  11418. ((let*) (tree-memq subst-loc (car vars)))
  11419. (else #f)))
  11420. (tree-subst-eq `(if ,expr ,@(cadr diff)) subst-loc true)
  11421. `(let ((_1_ ,expr))
  11422. ,(tree-subst-eq `(if _1_ ,@(cadr diff)) subst-loc true)))))))
  11423. ;; also not any-macro? (car true|false) probably
  11424. ;; (if x (set! y #t) (set! y #f)) -> (set! y x)
  11425. (lint-format "perhaps ~A" caller
  11426. (lists->string form
  11427. (cond ((eq? true-op (caadr diff)) ; very common!
  11428. ;; (if x (f y) (g y)) -> ((if x f g) y)
  11429. ;; but f and g can't be or/and unless there are no expressions
  11430. ;; I now like all of these -- originally found them odd: CL influence!
  11431. (if (equal? true-op test)
  11432. `((or ,test ,false-op) ,@true-rest)
  11433. `((if ,test ,true-op ,false-op) ,@true-rest)))
  11434. ((and (eq? (caadr diff) #t)
  11435. (not (cadadr diff)))
  11436. ;; (if x (set! y #t) (set! y #f)) -> (set! y x)
  11437. (tree-subst-eq test subst-loc true))
  11438. ((and (not (caadr diff))
  11439. (eq? (cadadr diff) #t))
  11440. ;; (if x (set! y #f) (set! y #t)) -> (set! y (not x))
  11441. (tree-subst-eq (simplify-boolean `(not ,expr) () () env)
  11442. subst-loc true))
  11443. ((equal? (caadr diff) test)
  11444. ;; (if x (set! y x) (set! y 21)) -> (set! y (or x 21))
  11445. (tree-subst-eq (simplify-boolean `(or ,@(cadr diff)) () () env)
  11446. subst-loc true))
  11447. ((or (memq true-op '(set! begin and or))
  11448. (let list-memq ((a subst-loc) (lst true))
  11449. (and (pair? lst)
  11450. (or (eq? a lst)
  11451. (list-memq a (cdr lst))))))
  11452. ;; (if x (set! y z) (set! y w)) -> (set! y (if x z w))
  11453. ;; true op moved out, if expr moved in
  11454. ;; (if A (and B C) (and B D)) -> (and B (if A C D))
  11455. ;; here differ-in-one means that preceding/trailing stuff must subst-loc exactly
  11456. (tree-subst-eq `(if ,expr ,@(cadr diff)) subst-loc true))
  11457. ;; paranoia... normally the extra let is actually not needed,
  11458. ;; but it's very hard to distinguish the bad cases
  11459. (else
  11460. `(let ((_1_ ,expr))
  11461. ,(tree-subst-eq `(if _1_ ,@(cadr diff)) subst-loc true)))))))))
  11462. ;; else not pair? diff
  11463. (unless (memq true-op '(let let*))
  11464. ;; differ-in-trailers can (sometimes) take advantage of values
  11465. (let ((enddiff (let differ-in-trailers ((p true)
  11466. (q false)
  11467. (c 0))
  11468. (and (pair? p)
  11469. (pair? q)
  11470. (if (equal? (car p) (car q))
  11471. (differ-in-trailers (cdr p) (cdr q) (+ c 1))
  11472. (and (> c 1)
  11473. (let ((op (if (memq true-op '(and or + * begin max min)) true-op 'values)))
  11474. (list p
  11475. (if (null? (cdr p)) (car p) `(,op ,@p))
  11476. (if (null? (cdr q)) (car q) `(,op ,@q))))))))))
  11477. ;; (if A (+ B C E) (+ B D)) -> (+ B (if A (+ C E) D))
  11478. ;; if p/q null, don't change because for example
  11479. ;; (if A (or B C) (or B C D F)) can't be (or B C (if A ...))
  11480. ;; but if this were not and/or, it could be (+ B (if A C (values C D F)))
  11481. (if (pair? enddiff)
  11482. (lint-format "perhaps ~A" caller
  11483. (lists->string form (tree-subst `((if ,expr ,@(cdr enddiff))) (car enddiff) true)))
  11484. ;; differ-in-headers looks for equal trailers
  11485. ;; (if A (+ B B E C) (+ D D E C)) -> (+ (if A (+ B B) (+ D D)) E C)
  11486. ;; these are not always (read: almost never) an improvement
  11487. (when (and (eq? true-op false-op)
  11488. (not (eq? true-op 'values))
  11489. (or (not (eq? true-op 'set!))
  11490. (equal? (car true-rest) (car false-rest))))
  11491. (let ((headdiff (let differ-in-headers ((p true-rest)
  11492. (q false-rest)
  11493. (c 0)
  11494. (rp ())
  11495. (rq ()))
  11496. (and (pair? p)
  11497. (pair? q)
  11498. (if (equal? p q)
  11499. (and (> c 0) ; once case is handled elsewhere?
  11500. (list p (reverse rp) (reverse rq)))
  11501. (differ-in-headers (cdr p) (cdr q)
  11502. (+ c 1)
  11503. (cons (car p) rp) (cons (car q) rq)))))))
  11504. (when (pair? headdiff)
  11505. (let ((op (if (memq true-op '(and or + * begin max min)) true-op 'values)))
  11506. (let ((tp (if (null? (cdadr headdiff))
  11507. (caadr headdiff)
  11508. `(,op ,@(cadr headdiff))))
  11509. (tq (if (null? (cdaddr headdiff))
  11510. (caaddr headdiff)
  11511. `(,op ,@(caddr headdiff)))))
  11512. ;; (if A (+ B B E C) (+ D D E C)) -> (+ (if A (+ B B) (+ D D)) E C)
  11513. (lint-format "perhaps ~A" caller
  11514. (lists->string form
  11515. `(,true-op
  11516. (if ,expr ,tp ,tq)
  11517. ,@(car headdiff)))))))))))))))
  11518. ;; (when (and (pair? true)...)
  11519. ;; end tree-subst section
  11520. (unless (= last-if-line-number line-number)
  11521. (do ((iff form (cadddr iff))
  11522. (iffs 0 (+ iffs 1)))
  11523. ((not (and (<= iffs 2)
  11524. (pair? iff)
  11525. (= (length iff) 4)
  11526. (eq? (car iff) 'if)))
  11527. (when (or (> iffs 2)
  11528. (and (= iffs 2)
  11529. (pair? iff)
  11530. (= (length iff) 3)
  11531. (eq? (car iff) 'if)))
  11532. (set! last-if-line-number line-number)
  11533. ;; (if a b (if c d (if e f g))) -> (cond (a b) (c d) (e f) (else g))
  11534. (lint-format "perhaps use cond: ~A" caller
  11535. (lists->string form
  11536. `(cond ,@(do ((iff form (cadddr iff))
  11537. (clauses ()))
  11538. ((not (and (pair? iff)
  11539. (= (length iff) 4)
  11540. (eq? (car iff) 'if)))
  11541. (append (reverse clauses)
  11542. (if (and (pair? iff)
  11543. (= (length iff) 3)
  11544. (eq? (car iff) 'if))
  11545. `((,(cadr iff) ,@(unbegin (caddr iff))))
  11546. `((else ,@(unbegin iff))))))
  11547. (set! clauses (cons (cons (cadr iff) (unbegin (caddr iff))) clauses))))))))))
  11548. (if (never-false test)
  11549. (lint-format "if test is never false: ~A" caller (truncated-list->string form))
  11550. (if (and (never-true test) true) ; complain about (if #f #f) later
  11551. ;; (if #f x y)
  11552. (lint-format "if test is never true: ~A" caller (truncated-list->string form))))
  11553. (cond ((side-effect? test env))
  11554. ((or (equal? test true) ; (if x x y) -> (or x y)
  11555. (equal? expr true))
  11556. (lint-format "perhaps ~A" caller
  11557. (lists->string form
  11558. (simplify-boolean (if (eq? false 'no-false)
  11559. `(or ,expr #<unspecified>)
  11560. `(or ,expr ,false))
  11561. () () env))))
  11562. ((or (equal? test `(not ,true)) ; (if x (not x) y) -> (and (not x) y)
  11563. (equal? `(not ,test) true)) ; (if (not x) x y) -> (and x y)
  11564. (lint-format "perhaps ~A" caller
  11565. (lists->string form
  11566. (simplify-boolean (if (eq? false 'no-false)
  11567. `(and ,true #<unspecified>)
  11568. `(and ,true ,false))
  11569. () () env))))
  11570. ((or (equal? test false) ; (if x y x) -> (and x y)
  11571. (equal? expr false))
  11572. (lint-format "perhaps ~A" caller
  11573. (lists->string form (simplify-boolean `(and ,expr ,true) () () env))))
  11574. ((or (equal? `(not ,test) false) ; (if x y (not x)) -> (or (not x) y)
  11575. (equal? test `(not ,false))) ; (if (not x) y x) -> (or x y)
  11576. (lint-format "perhaps ~A" caller
  11577. (lists->string form (simplify-boolean `(or ,false ,true) () () env)))))
  11578. (when (and (pair? true)
  11579. (eq? true-op 'cond)
  11580. (not (eq? false-op 'cond))
  11581. (not (boolean? false))) ; these cases are handled elsewhere via or/and
  11582. ;; (if A (cond...) B) -> (cond ((not A) B) ...)
  11583. ;; if no false and cond is one-shot => this can be optimized to (cond ((and (not A) C) => ...))
  11584. (lint-format "perhaps ~A" caller
  11585. (let ((nexpr (simplify-boolean (list 'not expr) () () env))
  11586. (nfalse (if (eq? false 'no-false)
  11587. (if (eq? form lint-mid-form)
  11588. ()
  11589. '(#<unspecified>))
  11590. (list (if (and (pair? false)
  11591. (> (tree-leaves false) 100))
  11592. (if (pair? (car false))
  11593. (list (list (caar false) '...))
  11594. (list (car false) '...))
  11595. false)))))
  11596. (lists->string form `(cond (,nexpr ,@nfalse) ,@true-rest)))))
  11597. ;; true-op = case happens a lot, but never in a way that (not expr)->false can be combined in the case
  11598. (when (= len 4)
  11599. (when (and (pair? true)
  11600. (eq? true-op 'if))
  11601. (let ((true-test (car true-rest))
  11602. (true-true (cadr true-rest)))
  11603. (if (= (length true) 4)
  11604. (let ((true-false (caddr true-rest)))
  11605. (if (equal? expr (simplify-boolean `(not ,true-test) () () env))
  11606. ;; (if a (if (not a) B C) A) -> (if a C A)
  11607. (lint-format "perhaps ~A" caller
  11608. (lists->string form `(if ,expr ,true-false ,false))))
  11609. (if (equal? expr true-test)
  11610. ;; (if x (if x z w) y) -> (if x z y)
  11611. (lint-format "perhaps ~A" caller
  11612. (lists->string form `(if ,expr ,true-true ,false))))
  11613. (if (equal? false true-false)
  11614. ;; (if a (if b B A) A) -> (if (and a b) B A)
  11615. (lint-format "perhaps ~A" caller
  11616. (lists->string form
  11617. (simplify-boolean
  11618. (if (not false)
  11619. `(and ,expr ,true-test ,true-true)
  11620. `(if (and ,expr ,true-test) ,true-true ,false))
  11621. () () env)))
  11622. (if (equal? false true-true)
  11623. ;; (if a (if b A B) A) -> (if (and a (not b)) B A)
  11624. (lint-format "perhaps ~A" caller
  11625. (lists->string form
  11626. (simplify-boolean
  11627. (if (not false)
  11628. `(and ,expr (not ,true-test) ,true-false)
  11629. `(if (and ,expr (not ,true-test)) ,true-false ,false))
  11630. () () env)))))
  11631. ;; (if a (if b d e) (if c d e)) -> (if (if a b c) d e)? reversed does not happen.
  11632. ;; (if a (if b d) (if c d)) -> (if (if a b c) d)
  11633. ;; (if a (if b d e) (if (not b) d e)) -> (if (eq? (not a) (not b)) d e)
  11634. (when (and (pair? false)
  11635. (eq? false-op 'if)
  11636. (= (length false) 4)
  11637. (not (equal? true-test (car false-rest)))
  11638. (equal? (cdr true-rest) (cdr false-rest)))
  11639. (let ((false-test (car false-rest)))
  11640. (lint-format "perhaps ~A" caller
  11641. (lists->string form
  11642. (cond ((and (pair? true-test)
  11643. (eq? (car true-test) 'not)
  11644. (equal? (cadr true-test) false-test))
  11645. `(if (not (eq? (not ,expr) ,true-test))
  11646. ,@(cdr true-rest)))
  11647. ((and (pair? false-test)
  11648. (eq? (car false-test) 'not)
  11649. (equal? true-test (cadr false-test)))
  11650. `(if (eq? (not ,expr) ,false-test)
  11651. ,@(cdr true-rest)))
  11652. ((> (+ (tree-leaves expr)
  11653. (tree-leaves true-test)
  11654. (tree-leaves false-test))
  11655. 12)
  11656. `(let ((_1_ (if ,expr ,true-test ,false-test)))
  11657. (if _1_ ,@(cdr true-rest))))
  11658. (else
  11659. `(if (if ,expr ,true-test ,false-test) ,@(cdr true-rest)))))))))
  11660. (begin ; (length true) != 4
  11661. (if (equal? expr (simplify-boolean `(not ,true-test) () () env))
  11662. (lint-format "perhaps ~A" caller ; (if a (if (not a) B) A) -> (if (not a) A)
  11663. (lists->string form `(if (not ,expr) ,false))))
  11664. (if (equal? expr true-test) ; (if x (if x z) w) -> (if x z w)
  11665. (lint-format "perhaps ~A" caller
  11666. (lists->string form `(if ,expr ,true-true ,false))))
  11667. (if (equal? false true-true) ; (if a (if b A) A)
  11668. (lint-format "perhaps ~A" caller
  11669. (let ((nexpr (simplify-boolean `(or (not ,expr) ,true-test) () () env)))
  11670. (lists->string form `(if ,nexpr ,false)))))))))
  11671. (when (pair? false)
  11672. (case false-op
  11673. ((cond) ; (if a A (cond...)) -> (cond (a A) ...)
  11674. (lint-format "perhaps ~A" caller (lists->string form `(cond (,expr ,true) ,@false-rest))))
  11675. ((if)
  11676. (when (= (length false) 4)
  11677. (let ((false-test (car false-rest))
  11678. (false-true (cadr false-rest))
  11679. (false-false (caddr false-rest)))
  11680. (if (equal? true false-true)
  11681. ;; (if a A (if b A B)) -> (if (or a b) A B)
  11682. (lint-format "perhaps ~A" caller
  11683. (if (and (pair? false-false)
  11684. (eq? (car false-false) 'if)
  11685. (equal? true (caddr false-false)))
  11686. (lists->string form
  11687. (let ((nexpr (simplify-boolean
  11688. `(or ,expr ,false-test ,(cadr false-false))
  11689. () () env)))
  11690. `(if ,nexpr ,true ,@(cdddr false-false))))
  11691. (if true
  11692. (let ((nexpr (simplify-boolean `(or ,expr ,false-test) () () env)))
  11693. (lists->string form `(if ,nexpr ,true ,false-false)))
  11694. (lists->string form
  11695. (simplify-boolean
  11696. `(and (not (or ,expr ,false-test)) ,false-false)
  11697. () () env)))))
  11698. (if (equal? true false-false)
  11699. ;; (if a A (if b B A)) -> (if (or a (not b)) A B)
  11700. (lint-format "perhaps ~A" caller
  11701. (if true
  11702. (let ((nexpr (simplify-boolean `(or ,expr (not ,false-test)) () () env)))
  11703. (lists->string form `(if ,nexpr ,true ,false-true)))
  11704. (lists->string form
  11705. (simplify-boolean
  11706. `(and (not (or ,expr (not ,false-test))) ,false-true)
  11707. () () env))))))))
  11708. (if (and (pair? true)
  11709. (eq? true-op 'if)
  11710. (= (length true) 3)
  11711. (= (length false) 3)
  11712. (equal? (cdr true-rest) (cdr false-rest)))
  11713. ;; (if a (if b d) (if c d)) -> (if (if a b c) d)
  11714. (lint-format "perhaps ~A" caller
  11715. (lists->string form
  11716. (if (> (+ (tree-leaves expr)
  11717. (tree-leaves (car true-rest))
  11718. (tree-leaves (car false-rest)))
  11719. 12)
  11720. `(let ((_1_ (if ,expr ,(car true-rest) ,(car false-rest))))
  11721. (if _1_ ,@(cdr true-rest)))
  11722. `(if (if ,expr ,(car true-rest) ,(car false-rest)) ,@(cdr true-rest)))))))
  11723. ((map) ; (if (null? x) () (map abs x)) -> (map abs x)
  11724. (if (and (pair? test)
  11725. (eq? (car test) 'null?)
  11726. (or (null? true)
  11727. (equal? true (cadr test)))
  11728. (equal? (cadr test) (cadr false-rest))
  11729. (or (null? (cddr false-rest))
  11730. (not (side-effect? (cddr false-rest) env))))
  11731. (lint-format "perhaps ~A" caller (lists->string form false))))
  11732. ((case)
  11733. (if (and (pair? expr)
  11734. (cond-eqv? expr (car false-rest) #t))
  11735. ;; (if (eof-object? x) 32 (case x ((#\a) 3) (else 4))) -> (case x ((#<eof>) 32) ((#\a) 3) (else 4))
  11736. (lint-format "perhaps ~A" caller
  11737. (lists->string form `(case ,(car false-rest)
  11738. ,(case-branch expr (car false-rest) (list true))
  11739. ,@(cdr false-rest))))))))
  11740. ) ; (= len 4)
  11741. (if (pair? false)
  11742. (let ((false-test (and (pair? false-rest) (car false-rest))))
  11743. (if (and (eq? false-op 'if) ; (if x 3 (if (not x) 4)) -> (if x 3 4)
  11744. (pair? false-rest)
  11745. (not (side-effect? test env)))
  11746. (if (or (equal? test false-test)
  11747. (equal? expr false-test))
  11748. (lint-format "perhaps ~A" caller (lists->string form `(if ,expr ,true ,@(cddr false-rest))))
  11749. (if (and (pair? false-test)
  11750. (eq? (car false-test) 'not)
  11751. (or (equal? test (cadr false-test))
  11752. (equal? expr (cadr false-test))))
  11753. (lint-format "perhaps ~A" caller (lists->string form `(if ,expr ,true ,(cadr false-rest)))))))
  11754. (if (and (eq? false-op 'if) ; (if test0 expr (if test1 expr)) -> if (or test0 test1) expr)
  11755. (null? (cddr false-rest)) ; other case is dealt with above
  11756. (equal? true (cadr false-rest)))
  11757. (let ((test1 (simplify-boolean `(or ,expr ,false-test) () () env)))
  11758. (lint-format "perhaps ~A" caller (lists->string form `(if ,test1 ,true ,@(cddr false-rest)))))))
  11759. (when (and (eq? false 'no-false) ; no false branch
  11760. (pair? true))
  11761. (when (pair? test)
  11762. (let ((test-op (car test)))
  11763. ;; the min+max case is seldom hit, and takes about 50 lines
  11764. (when (and (memq test-op '(< > <= >=))
  11765. (null? (cdddr test)))
  11766. (let ((rel-arg1 (cadr test))
  11767. (rel-arg2 (caddr test)))
  11768. ;; (if (< x y) (set! x y) -> (set! x (max x y))
  11769. (if (eq? true-op 'set!)
  11770. (let ((settee (car true-rest))
  11771. (setval (cadr true-rest)))
  11772. (if (and (member settee test)
  11773. (member setval test)) ; that's all there's room for
  11774. (let ((f (if (equal? settee (if (memq test-op '(< <=)) rel-arg1 rel-arg2)) 'max 'min)))
  11775. (lint-format "perhaps ~A" caller
  11776. (lists->string form `(set! ,settee (,f ,@true-rest)))))))
  11777. ;; (if (<= (list-ref ind i) 32) (list-set! ind i 32)) -> (list-set! ind i (max (list-ref ind i) 32))
  11778. (if (memq true-op '(list-set! vector-set!))
  11779. (let ((settee (car true-rest))
  11780. (index (cadr true-rest))
  11781. (setval (caddr true-rest)))
  11782. (let ((mx-op (if (and (equal? setval rel-arg1)
  11783. (eqv? (length rel-arg2) 3)
  11784. (equal? settee (cadr rel-arg2))
  11785. (equal? index (caddr rel-arg2)))
  11786. (if (memq test-op '(< <=)) 'min 'max)
  11787. (and (equal? setval rel-arg2)
  11788. (eqv? (length rel-arg1) 3)
  11789. (equal? settee (cadr rel-arg1))
  11790. (equal? index (caddr rel-arg1))
  11791. (if (memq test-op '(< <=)) 'max 'min)))))
  11792. (if mx-op
  11793. (lint-format "perhaps ~A" caller
  11794. (lists->string form `(,true-op ,settee ,index (,mx-op ,@(cdr test))))))))))))))
  11795. (cond ((not (eq? (car true) 'if)) ; (if test0 (if test1 expr)) -> (if (and test0 test1) expr)
  11796. (if (memq true-op '(when unless)) ; (if test0 (when test1 expr...)) -> (when (and test0 test1) expr...)
  11797. (let ((test1 (simplify-boolean (if (eq? true-op 'when)
  11798. `(and ,expr ,(car true-rest))
  11799. `(and ,expr (not ,(car true-rest))))
  11800. () () env)))
  11801. ;; (if (and (< x 1) y) (when z (display z) x)) -> (when (and (< x 1) y z) (display z) x)
  11802. (lint-format "perhaps ~A" caller
  11803. (lists->string form
  11804. (if (and (pair? test1)
  11805. (eq? (car test1) 'not))
  11806. `(unless ,(cadr test1) ,@(cdr true-rest))
  11807. `(when ,test1 ,@(cdr true-rest))))))))
  11808. ((null? (cddr true-rest))
  11809. (let ((test1 (simplify-boolean `(and ,expr ,(car true-rest)) () () env)))
  11810. (lint-format "perhaps ~A" caller (lists->string form `(if ,test1 ,(cadr true-rest))))))
  11811. ((equal? expr (car true-rest))
  11812. (lint-format "perhaps ~A" caller (lists->string form true)))
  11813. ((equal? (car true-rest) `(not ,expr))
  11814. (lint-format "perhaps ~A" caller
  11815. (lists->string form (caddr true-rest)))))))
  11816. (if (and (pair? test)
  11817. (memq (car test) '(< <= > >= =)) ; (if (< x y) x y) -> (min x y)
  11818. (null? (cdddr test))
  11819. (member false test)
  11820. (member true test))
  11821. (if (eq? (car test) '=) ; (if (= x y) y x) -> y [this never happens]
  11822. (lint-format "perhaps ~A" caller (lists->string form false))
  11823. (let ((f (if (equal? (cadr test) (if (memq (car test) '(< <=)) true false))
  11824. 'min 'max)))
  11825. (lint-format "perhaps ~A" caller (lists->string form `(,f ,true ,false))))))
  11826. (cond ((eq? expr #t) ; (if #t #f) -> #f
  11827. (lint-format "perhaps ~A" caller (lists->string form true)))
  11828. ((not expr)
  11829. (if (eq? false 'no-false)
  11830. (if true ; (if #f x) as a kludgey #<unspecified>
  11831. (lint-format "perhaps ~A" caller (lists->string form #<unspecified>)))
  11832. ;; (if (negative? (gcd x y)) a b) -> b
  11833. (lint-format "perhaps ~A" caller (lists->string form false))))
  11834. ((not (equal? true false))
  11835. (if (boolean? true)
  11836. (if (boolean? false) ; ! (if expr #t #f) turned into something less verbose
  11837. ;; (if x #f #t) -> (not x)
  11838. (lint-format "perhaps ~A" caller
  11839. (lists->string form (if true
  11840. expr
  11841. (simplify-boolean `(not ,expr) () () env))))
  11842. (when (= suggestion made-suggestion)
  11843. ;; (if x #f y) -> (and (not x) y)
  11844. (lint-format "perhaps ~A" caller
  11845. (lists->string form (if true
  11846. (if (eq? false 'no-false)
  11847. expr
  11848. (simplify-boolean `(or ,expr ,false) () () env))
  11849. (simplify-boolean
  11850. (if (eq? false 'no-false)
  11851. `(not ,expr)
  11852. `(and (not ,expr) ,false))
  11853. () () env))))))
  11854. (if (and (boolean? false)
  11855. (= suggestion made-suggestion))
  11856. ;; (if x y #t) -> (or (not x) y)
  11857. (lint-format "perhaps ~A" caller
  11858. (let ((nexpr (if false
  11859. (if (and (pair? expr) (eq? (car expr) 'not))
  11860. `(or ,(cadr expr) ,true)
  11861. `(or (not ,expr) ,true))
  11862. `(and ,expr ,true))))
  11863. (lists->string form (simplify-boolean nexpr () () env)))))))
  11864. ((= len 4)
  11865. ;; (if x (+ y 1) (+ y 1)) -> (+ y 1)
  11866. (lint-format "if is not needed here: ~A" caller
  11867. (lists->string form (if (not (side-effect? test env))
  11868. true
  11869. `(begin ,expr ,true))))))
  11870. (when (and (= suggestion made-suggestion)
  11871. (not (equal? expr test))) ; make sure the boolean simplification gets reported
  11872. ;; (or (not (pair? x)) (not (pair? z))) -> (not (and (pair? x) (pair? z)))
  11873. (lint-format "perhaps ~A" caller (lists->string test expr)))
  11874. (when (pair? true)
  11875. (if (and (pair? test)
  11876. (pair? true-rest)
  11877. (null? (cdr true-rest))
  11878. (or (equal? test (car true-rest))
  11879. (equal? expr (car true-rest))))
  11880. (lint-format "perhaps ~A" caller
  11881. (lists->string form
  11882. (if (eq? false 'no-false)
  11883. `(cond (,expr => ,true-op))
  11884. `(cond (,expr => ,true-op) (else ,false))))))
  11885. (when (and (pair? false)
  11886. (eq? true-op 'if)
  11887. (eq? false-op 'if)
  11888. (= (length true) (length false) 4)
  11889. (equal? (car true-rest) (car false-rest)))
  11890. (if (and (equal? (cadr true-rest) (caddr false-rest)) ; (if A (if B a b) (if B b a)) -> (if (eq? (not A) (not B)) a b)
  11891. (equal? (caddr true-rest) (cadr false-rest)))
  11892. (let* ((switch #f)
  11893. (a (if (and (pair? expr)
  11894. (eq? (car expr) 'not))
  11895. (begin (set! switch #t) expr)
  11896. (simplify-boolean `(not ,expr) () () env)))
  11897. (b (if (and (pair? (car true-rest))
  11898. (eq? (caar true-rest) 'not))
  11899. (begin (set! switch (not switch)) (car true-rest))
  11900. (simplify-boolean `(not ,(car true-rest)) () () env))))
  11901. (lint-format "perhaps ~A" caller
  11902. (lists->string form
  11903. (if switch
  11904. `(if (eq? ,a ,b) ,(cadr false-rest) ,(cadr true-rest))
  11905. `(if (eq? ,a ,b) ,(cadr true-rest) ,(cadr false-rest))))))
  11906. (unless (or (side-effect? expr env)
  11907. (equal? (cdr true-rest) (cdr false-rest))) ; handled elsewhere
  11908. (if (equal? (cadr true-rest) (cadr false-rest)) ; (if A (if B a b) (if B a c)) -> (if B a (if A b c))
  11909. (lint-format "perhaps ~A" caller
  11910. (lists->string form
  11911. `(if ,(car true-rest) ,(cadr true-rest)
  11912. (if ,expr ,(caddr true-rest) ,(caddr false-rest)))))
  11913. (if (equal? (caddr true-rest) (caddr false-rest)) ; (if A (if B a b) (if B c b)) -> (if B (if A a c) b)
  11914. (lint-format "perhaps ~A" caller
  11915. (lists->string form
  11916. `(if ,(car true-rest)
  11917. (if ,expr ,(cadr true-rest) ,(cadr false-rest))
  11918. ,(caddr true-rest))))))))))
  11919. ;; --------
  11920. (when (and (= suggestion made-suggestion)
  11921. (not (= line-number last-if-line-number)))
  11922. ;; unravel complicated if-then-else nestings into a single cond, if possible.
  11923. ;;
  11924. ;; The (> new-len *report-nested-if*) below can mean (nearly) all nested ifs are turned into conds.
  11925. ;; For a long time I thought the if form was easier to read, but now
  11926. ;; I like cond better. But cond also has serious readability issues:
  11927. ;; it needs to be clearer where the test separates from the result,
  11928. ;; and in a stack of these clauses, it's hard to see anything at all.
  11929. ;; Maybe a different color for the test than the result?
  11930. ;;
  11931. ;; Also, the check for tree-leaves being hugely different is taken
  11932. ;; from C -- I think it is easier to read a large if statement if
  11933. ;; the shortest clause is at the start -- especially in a nested if where
  11934. ;; it can be nearly impossible to see which dangling one-liner matches
  11935. ;; which if (this even in emacs because it unmarks or doesn't remark the matching
  11936. ;; paren as you're trying to scroll up to it).
  11937. ;;
  11938. ;; the cond form is not always an improvement:
  11939. ;; (if A (if B (if C a b) (if C c d)) (if B (if C e f) (if C g h)))
  11940. ;; (cond (A (cond (B (cond (C a) (else b))) ... oh forget it ...))))
  11941. ;; perhaps: (case (+ (if A 4 0) (if B 2 0) (if C 1 0)) ((#b000)...))!
  11942. ;; how often (and how deeply nested) does this happen? -- not very, but nesting can be ridiculous.
  11943. ;; and this assumes all tests are always hit
  11944. (define (swap-clauses form)
  11945. (if (not (pair? (cdddr form)))
  11946. form
  11947. (let ((expr (cadr form))
  11948. (ltrue (caddr form))
  11949. (lfalse (cadddr form)))
  11950. (let ((true-n (tree-leaves ltrue))
  11951. (false-n (if (not (pair? lfalse))
  11952. 1
  11953. (tree-leaves lfalse))))
  11954. (if (< false-n (/ true-n 4))
  11955. (let ((new-expr (simplify-boolean `(not ,expr) () () env)))
  11956. (if (and (pair? ltrue)
  11957. (eq? (car ltrue) 'if))
  11958. (set! ltrue (swap-clauses ltrue)))
  11959. (if (and (pair? ltrue)
  11960. (eq? (car ltrue) 'cond))
  11961. `(cond (,new-expr ,@(unbegin lfalse))
  11962. ,@(cdr ltrue))
  11963. `(cond (,new-expr ,@(unbegin lfalse))
  11964. (else ,@(unbegin ltrue)))))
  11965. (begin
  11966. (if (and (pair? lfalse)
  11967. (eq? (car lfalse) 'if))
  11968. (set! lfalse (swap-clauses lfalse)))
  11969. (if (and (pair? lfalse)
  11970. (eq? (car lfalse) 'cond))
  11971. `(cond (,expr ,@(unbegin ltrue))
  11972. ,@(cdr lfalse))
  11973. `(cond (,expr ,@(unbegin ltrue))
  11974. (else ,@(unbegin lfalse))))))))))
  11975. (let ((new-if (swap-clauses form)))
  11976. (if (eq? (car new-if) 'cond)
  11977. (if (> (length new-if) *report-nested-if*)
  11978. (begin
  11979. (set! last-if-line-number line-number)
  11980. (lint-format "perhaps ~A" caller (lists->string form new-if)))
  11981. (when (= len 4)
  11982. (let ((true-len (tree-leaves (caddr form))))
  11983. (if (and (> true-len *report-short-branch*)
  11984. (< (tree-leaves (cadddr form)) (/ true-len *report-short-branch*)))
  11985. (let ((new-expr (simplify-boolean `(not ,(cadr form)) () () env)))
  11986. (lint-format "perhaps place the much shorter branch first~A: ~A" caller
  11987. (local-line-number (cadr form))
  11988. (truncated-lists->string form `(if ,new-expr ,false ,true))))))))))
  11989. ;; if+let() -> when: about a dozen hits
  11990. (let ((ntrue (and (pair? true) ; (if A B (let () (display x))) -> (if A B (begin (display x)))
  11991. (eq? true-op 'let)
  11992. (pair? (cdr true))
  11993. (null? (cadr true))
  11994. (not (tree-table-member definers (cddr true)))
  11995. (cddr true)))
  11996. (nfalse (and (pair? false)
  11997. (eq? false-op 'let)
  11998. (pair? (cdr false))
  11999. (null? (cadr false))
  12000. (not (tree-table-member definers (cddr false)))
  12001. (cddr false))))
  12002. (if (or ntrue nfalse)
  12003. (lint-format "perhaps ~A" caller
  12004. (lists->string form
  12005. (if (eq? false 'no-false)
  12006. `(when ,expr ,@ntrue)
  12007. (if ntrue
  12008. (if nfalse
  12009. `(if ,expr (begin ,@ntrue) (begin ,@nfalse))
  12010. `(if ,expr (begin ,@ntrue) ,false))
  12011. `(if ,expr ,true (begin ,@nfalse))))))))
  12012. (when (= len 4)
  12013. ;; move repeated test to top, if no inner false branches
  12014. ;; (if A (if B C) (if B D)) -> (if B (if A C D))
  12015. (when (and (pair? true)
  12016. (pair? false)
  12017. (eq? true-op 'if)
  12018. (eq? false-op 'if)
  12019. (equal? (car true-rest) (car false-rest))
  12020. (null? (cddr true-rest))
  12021. (null? (cddr false-rest)))
  12022. (lint-format "perhaps ~A" caller
  12023. (lists->string form `(if ,(car true-rest)
  12024. (if ,expr
  12025. ,(cadr true-rest)
  12026. ,(cadr false-rest))))))
  12027. ;; move repeated start/end statements out of the if
  12028. (let ((ltrue (if (and (pair? true) (eq? true-op 'begin)) true (list 'begin true)))
  12029. (lfalse (if (and (pair? false) (eq? false-op 'begin)) false (list 'begin false))))
  12030. (let ((true-len (length ltrue))
  12031. (false-len (length lfalse)))
  12032. (let ((start (if (and (equal? (cadr ltrue) (cadr lfalse))
  12033. (not (side-effect? expr env))) ; expr might affect start, so we can't pull it ahead
  12034. (list (cadr ltrue))
  12035. ()))
  12036. (end (if (and (not (= true-len false-len 2))
  12037. (equal? (list-ref ltrue (- true-len 1))
  12038. (list-ref lfalse (- false-len 1))))
  12039. (list (list-ref ltrue (- true-len 1)))
  12040. ())))
  12041. (when (or (pair? start)
  12042. (pair? end))
  12043. (let ((new-true (cdr ltrue))
  12044. (new-false (cdr lfalse)))
  12045. (when (pair? end)
  12046. (set! new-true (copy new-true (make-list (- true-len 2)))) ; (copy lst ()) -> ()
  12047. (set! new-false (copy new-false (make-list (- false-len 2)))))
  12048. (when (pair? start)
  12049. (if (pair? new-true) (set! new-true (cdr new-true)))
  12050. (if (pair? new-false) (set! new-false (cdr new-false))))
  12051. (when (or (pair? end)
  12052. (and (pair? new-true)
  12053. (pair? new-false))) ; otherwise the rewrite changes the returned value
  12054. (if (pair? new-true)
  12055. (set! new-true (if (null? (cdr new-true))
  12056. (car new-true)
  12057. (cons 'begin new-true))))
  12058. (if (pair? new-false)
  12059. (set! new-false (if (null? (cdr new-false))
  12060. (car new-false)
  12061. (cons 'begin new-false))))
  12062. ;; (if x (display y) (begin (set! z y) (display y))) -> (begin (if (not x) (set! z y)) (display y))
  12063. (lint-format "perhaps ~A" caller
  12064. (lists->string form
  12065. (let ((body (if (null? new-true)
  12066. `(if (not ,expr) ,new-false)
  12067. (if (null? new-false)
  12068. `(if ,expr ,new-true)
  12069. `(if ,expr ,new-true ,new-false)))))
  12070. `(begin ,@start
  12071. ,body
  12072. ,@end))))))))))
  12073. (when (and (= suggestion made-suggestion) ; not redundant -- this will repeat the earlier suggestion in many cases
  12074. (not (= line-number last-if-line-number))
  12075. (pair? expr) ; (if (not a) A B) -> (if a B A)
  12076. (eq? (car expr) 'not)
  12077. (> (tree-leaves true) (tree-leaves false)))
  12078. (lint-format "perhaps ~A" caller
  12079. (lists->string form `(if ,(cadr expr) ,false ,true))))
  12080. ;; this happens occasionally -- scarcely worth this much code! (gather copied vars outside the if)
  12081. (when (and (pair? true)
  12082. (pair? false)
  12083. (eq? true-op 'let)
  12084. (eq? false-op 'let)
  12085. (pair? (car true-rest))
  12086. (pair? (car false-rest)))
  12087. (let ((true-vars (map car (car true-rest)))
  12088. (false-vars (map car (car false-rest)))
  12089. (shared-vars ()))
  12090. (for-each (lambda (v)
  12091. (if (and (memq v false-vars)
  12092. (equal? (cadr (assq v (car true-rest)))
  12093. (cadr (assq v (car false-rest)))))
  12094. (set! shared-vars (cons v shared-vars))))
  12095. true-vars)
  12096. (when (pair? shared-vars)
  12097. ;; now remake true/false lets (maybe nil) without shared-vars
  12098. (let ((ntv ())
  12099. (nfv ())
  12100. (sv ()))
  12101. (for-each (lambda (v)
  12102. (if (memq (car v) shared-vars)
  12103. (set! sv (cons v sv))
  12104. (set! ntv (cons v ntv))))
  12105. (car true-rest))
  12106. (set! ntv (if (or (pair? ntv)
  12107. (pair? (cddr true-rest))) ; even define is safe here because outer let blocks it just as inner let used to
  12108. `(let ,(reverse ntv) ,@(cdr true-rest))
  12109. (cadr true-rest)))
  12110. (for-each (lambda (v)
  12111. (if (not (memq (car v) shared-vars))
  12112. (set! nfv (cons v nfv))))
  12113. (car false-rest))
  12114. (set! nfv (if (or (pair? nfv)
  12115. (pair? (cddr false-rest)))
  12116. `(let ,(reverse nfv) ,@(cdr false-rest))
  12117. (cadr false-rest)))
  12118. ;; (if (> (+ a b) 3) (let ((a x) (c y)) (* a (log c))) (let ((b z) (c y)) (+... ->
  12119. ;; (let ((c y)) (if (> (+ a b) 3) (let ((a x)) (* a (log c))) (let ((b z)) (+ b (log c)))))
  12120. (lint-format "perhaps ~A" caller
  12121. (lists->string form
  12122. (if (not (or (side-effect? expr env)
  12123. (tree-set-member (map car sv) expr)))
  12124. `(let ,(reverse sv) (if ,expr ,ntv ,nfv))
  12125. (let ((uniq (find-unique-name form)))
  12126. `(let ((,uniq ,expr))
  12127. (let ,(reverse sv)
  12128. (if ,uniq ,ntv ,nfv))))))))))))) ; (when (and (= suggestion made-suggestion)...))
  12129. (when (and *report-one-armed-if*
  12130. (eq? false 'no-false)
  12131. (or (not (integer? *report-one-armed-if*))
  12132. (> (tree-leaves true) *report-one-armed-if*)))
  12133. ;; (if a (begin (set! x y) z)) -> (when a (set! x y) z)
  12134. (lint-format "~A~A~A perhaps ~A" caller
  12135. (if (integer? *report-one-armed-if*)
  12136. "this one-armed if is too big"
  12137. "")
  12138. (local-line-number test)
  12139. (if (integer? *report-one-armed-if*) ";" "")
  12140. (truncated-lists->string
  12141. form (if (and (pair? expr)
  12142. (eq? (car expr) 'not))
  12143. `(unless ,(cadr expr) ,@(unbegin true))
  12144. `(when ,expr ,@(unbegin true))))))
  12145. (if (symbol? expr)
  12146. (set-ref expr caller form env)
  12147. (lint-walk caller expr env))
  12148. (if (symbol? true)
  12149. (set-ref true caller form env)
  12150. (set! env (lint-walk caller true env)))
  12151. (if (symbol? false)
  12152. (if (not (eq? false 'no-false))
  12153. (set-ref false caller form env))
  12154. (set! env (lint-walk caller false env))))))
  12155. env))
  12156. (hash-table-set! h 'if if-walker))
  12157. ;; -------- when, unless --------
  12158. (let ()
  12159. (define (when-walker caller form env)
  12160. (if (< (length form) 3)
  12161. (begin
  12162. (lint-format "~A is messed up: ~A" caller (car form) (truncated-list->string form))
  12163. env)
  12164. (let ((test (cadr form))
  12165. (head (car form)))
  12166. (if (and (pair? test)
  12167. (eq? (car test) 'not))
  12168. ;; (when (not a) (set! x y)) -> (unless a (set! x y))
  12169. (lint-format "perhaps ~A"
  12170. caller
  12171. (truncated-lists->string form
  12172. `(,(if (eq? head 'when) 'unless 'when)
  12173. ,(cadr test)
  12174. ,@(cddr form)))))
  12175. (if (never-false test)
  12176. (lint-format "~A test is never false: ~A" caller head (truncated-list->string form))
  12177. (if (never-true test) ; (unless #f...)
  12178. (lint-format "~A test is never true: ~A" caller head (truncated-list->string form))))
  12179. (if (symbol? test)
  12180. (begin
  12181. (set-ref test caller form env)
  12182. (if (and (eq? head 'when)
  12183. (pair? (cddr form))
  12184. (pair? (caddr form)))
  12185. (if (memq test (caddr form))
  12186. (and-incomplete form head test (caddr form) env)
  12187. (do ((p (caddr form) (cdr p)))
  12188. ((or (not (pair? p))
  12189. (and (pair? (car p))
  12190. (memq test (car p))))
  12191. (if (pair? p)
  12192. (and-incomplete form head test (car p) env)))))))
  12193. (when (pair? test)
  12194. (if (and (eq? (car test) 'and)
  12195. (pair? (cdr test))
  12196. (pair? (cddr test))
  12197. (null? (cdddr test)))
  12198. (let ((arg1 (cadr test))
  12199. (arg2 (caddr test)))
  12200. (if (or (and (pair? arg1)
  12201. (eq? (car arg1) 'not))
  12202. (and (pair? arg2)
  12203. (eq? (car arg2) 'not)))
  12204. (if (eq? head 'unless)
  12205. ;; (unless (and x (not y)) (display z)) -> (when (or (not x) y) ...)
  12206. (lint-format "perhaps ~A" caller
  12207. (lists->string form `(when ,(simplify-boolean `(not ,test) () () env) ...)))
  12208. (if (and (pair? arg1)
  12209. (eq? (car arg1) 'not)
  12210. (pair? arg2)
  12211. (eq? (car arg2) 'not))
  12212. ;; (when (and (not x) (not y)) (display z)) -> (unless (or x y) ...)
  12213. (lint-format "perhaps ~A" caller
  12214. (lists->string form `(unless (or ,(cadr arg1) ,(cadr arg2)) ...))))))))
  12215. (lint-walk caller test env)))
  12216. (when (and (pair? (cddr form)) ; (when t1 (if t2 A)) -> (when (and t1 t2) A)
  12217. (null? (cdddr form))
  12218. (pair? (caddr form)))
  12219. (let ((body (caddr form)))
  12220. (if (eq? (car body) 'cond) ; (when (cond ...)) -> (cond ...)
  12221. (lint-format "perhaps ~A" caller
  12222. (truncated-lists->string form
  12223. `(cond (,(if (eq? (car form) 'when)
  12224. (simplify-boolean `(not ,(cadr form)) () () env)
  12225. (cadr form))
  12226. #f)
  12227. ,@(cdr body))))
  12228. (when (or (memq (car body) '(when unless))
  12229. (and (eq? (car body) 'if)
  12230. (pair? (cdr body))
  12231. (pair? (cddr body))
  12232. (null? (cdddr body))))
  12233. (let ((new-test (let ((inner-test (if (eq? (car body) 'unless)
  12234. `(not ,(cadr body))
  12235. (cadr body)))
  12236. (outer-test (if (eq? head 'unless)
  12237. `(not ,test)
  12238. test)))
  12239. (simplify-boolean `(and ,outer-test ,inner-test) () () env))))
  12240. ;; (when (and (< x 1) y) (if z (display z))) -> (when (and (< x 1) y z) (display z))
  12241. (lint-format "perhaps ~A" caller
  12242. (lists->string form
  12243. (if (and (pair? new-test)
  12244. (eq? (car new-test) 'not))
  12245. `(unless ,(cadr new-test) ,@(cddr body))
  12246. `(when ,new-test ,@(cddr body))))))))))
  12247. (lint-walk-open-body caller head (cddr form) env))))
  12248. (hash-table-set! h 'when when-walker)
  12249. (hash-table-set! h 'unless when-walker))
  12250. ;; ---------------- cond ----------------
  12251. (let ()
  12252. (define (cond-walker caller form env)
  12253. (let ((ctr 0)
  12254. (suggest made-suggestion)
  12255. (len (- (length form) 1)))
  12256. (if (< len 1)
  12257. (lint-format "cond is messed up: ~A" caller (truncated-list->string form))
  12258. (let ((exprs ())
  12259. (result :unset)
  12260. (has-else #f)
  12261. (has-combinations #f)
  12262. (simplifications ())
  12263. (prev-clause #f)
  12264. (all-eqv #t)
  12265. (eqv-select #f))
  12266. ;; (cond (A (and B C)) (else (and B D))) et al never happens
  12267. ;; also (cond (A C) (B C)) -> (if (or A B) C) [non-pair C]
  12268. ;; ----------------
  12269. ;; if regular cond + else
  12270. ;; scan all return blocks
  12271. ;; if all one form, and either header or trailer always match,
  12272. ;; rewrite as header + cond|if + trailer
  12273. ;; given values and the presence of else, every possibility is covered
  12274. ;; at least (car result) has to match across all
  12275. (when (and (> len 1) ; (cond (else ...)) is handled elsewhere
  12276. (pair? (cdr form))
  12277. (pair? (cadr form))
  12278. (not (tree-set-member '(unquote #_{list}) form)))
  12279. (let ((first-clause (cadr form))
  12280. (else-clause (list-ref form len)))
  12281. (when (and (pair? (cdr first-clause))
  12282. (null? (cddr first-clause))
  12283. (pair? (cadr first-clause))
  12284. (pair? else-clause))
  12285. (let ((first-result (cadr first-clause))
  12286. (first-func (caadr first-clause)))
  12287. (if (and (memq (car else-clause) '(#t else))
  12288. (pair? (cdr else-clause))
  12289. (pair? (cadr else-clause))
  12290. (or (equal? (caadr first-clause) (caadr else-clause)) ; there's some hope we'll match
  12291. (escape? (cadr else-clause) env)))
  12292. (let ((else-error (escape? (cadr else-clause) env)))
  12293. (when (and (pair? (cdr first-result))
  12294. (not (eq? first-func 'values))
  12295. (or (not (hash-table-ref syntaces first-func))
  12296. (eq? first-func 'set!))
  12297. (every? (lambda (c)
  12298. (and (pair? c)
  12299. (pair? (cdr c))
  12300. (pair? (cadr c))
  12301. (null? (cddr c))
  12302. (pair? (cdadr c))
  12303. (or (equal? first-func (caadr c))
  12304. (and (eq? c else-clause)
  12305. else-error))))
  12306. (cddr form)))
  12307. ((lambda (header-len trailer-len result-min-len)
  12308. (when (and (or (not (eq? first-func 'set!))
  12309. (> header-len 1))
  12310. (or (not (eq? first-func '/))
  12311. (> header-len 1)
  12312. (> trailer-len 0)))
  12313. (let ((header (copy first-result (make-list header-len)))
  12314. (trailer (copy first-result (make-list trailer-len) (- (length first-result) trailer-len))))
  12315. (if (= len 2)
  12316. (unless (equal? first-result (cadr else-clause)) ; handled elsewhere (all results equal -> result)
  12317. ;; (cond (x (for-each (lambda (x) (display (+ x a))) (f y))) (else (for-each... ->
  12318. ;; (for-each (lambda (x) (display (+ x a))) (if x (f y) (g y)))
  12319. (lint-format "perhaps ~A" caller
  12320. (let ((else-result (cadr else-clause)))
  12321. (let ((first-mid-len (- (length first-result) header-len trailer-len))
  12322. (else-mid-len (- (length else-result) header-len trailer-len)))
  12323. (let ((fmid (if (= first-mid-len 1)
  12324. (list-ref first-result header-len)
  12325. `(values ,@(copy first-result (make-list first-mid-len) header-len))))
  12326. (emid (if else-error
  12327. else-result
  12328. (if (= else-mid-len 1)
  12329. (list-ref else-result header-len)
  12330. `(values ,@(copy else-result (make-list else-mid-len) header-len))))))
  12331. (lists->string form `(,@header (if ,(car first-clause) ,fmid ,emid) ,@trailer)))))))
  12332. ;; len > 2 so use cond in the revision
  12333. (let ((middle (map (lambda (c)
  12334. (if (and else-error
  12335. (eq? c else-clause))
  12336. else-clause
  12337. (let ((test (car c))
  12338. (result (cadr c)))
  12339. (let ((mid-len (- (length result) header-len trailer-len)))
  12340. `(,test ,(if (= mid-len 1)
  12341. (list-ref result header-len)
  12342. `(values ,@(copy result (make-list mid-len) header-len))))))))
  12343. (cdr form))))
  12344. ;; (cond ((< x 1) (+ x 1)) ((< y 1) (+ x 3)) (else (+ x 2))) -> (+ x (cond ((< x 1) 1) ((< y 1) 3) (else 2)))
  12345. (lint-format "perhaps ~A" caller
  12346. (lists->string form `(,@header (cond ,@middle) ,@trailer))))))))
  12347. (partition-form (cdr form) (if else-error (- len 1) len)))))
  12348. ;; not escaping else here because the trailing args might be evaluated first
  12349. (when (and (not (hash-table-ref syntaces (car first-result)))
  12350. (every? (lambda (c)
  12351. (and (pair? c)
  12352. (pair? (cdr c))
  12353. (pair? (cadr c))
  12354. (null? (cddr c))
  12355. (not (hash-table-ref syntaces (caadr c)))
  12356. (equal? (cdadr c) (cdr first-result))))
  12357. (cddr form)))
  12358. (if (every? (lambda (c)
  12359. (eq? first-func (caadr c))) ; all result clauses are the same!?
  12360. (cddr form)) ; possibly no else, so not always a duplicate message
  12361. ;; (cond (X (f y z)) (Y (f y z)) (Z (f y z))) -> (if (or X Y Z) (f y z))
  12362. (lint-format "perhaps ~A" caller
  12363. (lists->string form
  12364. `(if (or ,@(map car (cdr form)))
  12365. ,first-result)))
  12366. ;; here we need an else clause else (apply #<unspecified> args)
  12367. (if (memq (car else-clause) '(#t else))
  12368. ;; (cond (X (f y z)) (else (g y z))) -> ((cond (X f) (else g)) y z)
  12369. (lint-format "perhaps ~A" caller
  12370. (lists->string form
  12371. `((cond ,@(map (lambda (c)
  12372. (list (car c) (caadr c)))
  12373. (cdr form)))
  12374. ,@(cdr first-result))))))))))))
  12375. ;; ----------------
  12376. (let ((falses ())
  12377. (trues ()))
  12378. (for-each
  12379. (lambda (clause)
  12380. (set! ctr (+ ctr 1))
  12381. (if (not (pair? clause))
  12382. (begin
  12383. (set! all-eqv #f)
  12384. (set! has-combinations #f)
  12385. ;; ; (cond 1)
  12386. (lint-format "cond clause ~A in ~A is not a pair?" caller clause (truncated-list->string form)))
  12387. (begin
  12388. (when all-eqv
  12389. (unless eqv-select
  12390. (set! eqv-select (eqv-selector (car clause))))
  12391. (set! all-eqv (and eqv-select
  12392. (not (and (pair? (cdr clause))
  12393. (eq? (cadr clause) '=>))) ; case sends selector, but cond sends test result
  12394. (cond-eqv? (car clause) eqv-select #t))))
  12395. (if (and (pair? prev-clause)
  12396. (not has-combinations)
  12397. (> len 2)
  12398. (equal? (cdr clause) (cdr prev-clause)))
  12399. (if (memq (car clause) '(else #t)) ; (cond ... (x z) (else z)) -> (cond ... (else z))
  12400. (unless (side-effect? (car prev-clause) env)
  12401. ;; (cond (x y) (z 32) (else 32))
  12402. (lint-format* caller
  12403. "this clause could be omitted: "
  12404. (truncated-list->string prev-clause)))
  12405. (set! has-combinations #t))) ; handle these later
  12406. (set! prev-clause clause)
  12407. (let ((expr (simplify-boolean (car clause) trues falses env))
  12408. (test (car clause))
  12409. (sequel (cdr clause))
  12410. (first-sequel (and (pair? (cdr clause)) (cadr clause))))
  12411. (if (not (equal? expr test))
  12412. (set! simplifications (cons (cons clause expr) simplifications)))
  12413. (if (symbol? test)
  12414. (if (and (not (eq? test 'else))
  12415. (pair? first-sequel))
  12416. (if (memq test first-sequel)
  12417. (and-incomplete form 'cond test first-sequel env)
  12418. (do ((p first-sequel (cdr p)))
  12419. ((or (not (pair? p))
  12420. (and (pair? (car p))
  12421. (memq test (car p))))
  12422. (if (pair? p)
  12423. (and-incomplete form 'cond test (car p) env))))))
  12424. (if (and (pair? test)
  12425. (pair? first-sequel)
  12426. (hash-table-ref bools (car test)))
  12427. (if (member (cadr test) first-sequel)
  12428. (and-forgetful form 'cond test first-sequel env)
  12429. (do ((p first-sequel (cdr p)))
  12430. ((or (not (pair? p))
  12431. (and (pair? (car p))
  12432. (member (cadr test) (car p))))
  12433. (if (pair? p)
  12434. (and-forgetful form 'cond test (car p) env)))))))
  12435. ;; code here to check every arg against its use in the sequel found no problems?!?
  12436. (cond ((memq test '(else #t))
  12437. (set! has-else #t)
  12438. (when (pair? sequel)
  12439. (if (eq? first-sequel #<unspecified>)
  12440. ;; (cond ((= x y) z) (else #<unspecified>)
  12441. (lint-format "this #<unspecified> is redundant: ~A" caller clause))
  12442. (if (and (pair? first-sequel) ; (cond (a A) (else (cond ...))) -> (cond (a A) ...)
  12443. (null? (cdr sequel))) ; similarly for if, when, and unless
  12444. (case (car first-sequel)
  12445. ((cond)
  12446. ;; (cond ((< x 1) 2) (else (cond ((< y 3) 2) (#t 4))))
  12447. (lint-format "else clause could be folded into the outer cond: ~A" caller
  12448. (lists->string form (append (copy form (make-list ctr))
  12449. (cdr first-sequel)))))
  12450. ((if)
  12451. ;; (cond (a A) (else (if b B)))
  12452. (lint-format "else clause could be folded into the outer cond: ~A" caller
  12453. (lists->string form
  12454. (append (copy form (make-list ctr))
  12455. (if (= (length first-sequel) 3)
  12456. (list (cdr first-sequel))
  12457. `((,(cadr first-sequel) ,@(unbegin (caddr first-sequel)))
  12458. (else ,@(unbegin (cadddr first-sequel)))))))))
  12459. ((when unless)
  12460. ;; (cond (a A) (else (when b B)))
  12461. (lint-format "else clause could be folded into the outer cond: ~A" caller
  12462. (lists->string form
  12463. (append (copy form (make-list ctr))
  12464. (if (eq? (car first-sequel) 'when)
  12465. `((,(cadr first-sequel) ,@(cddr first-sequel)))
  12466. `(((not ,(cadr first-sequel)) ,@(cddr first-sequel))))))))))))
  12467. ((not (= ctr len)))
  12468. ((equal? test ''else)
  12469. ;; (cond (x y) ('else z))
  12470. (lint-format "odd cond clause test: is 'else supposed to be else? ~A" caller
  12471. (truncated-list->string clause)))
  12472. ((and (eq? test 't)
  12473. (not (var-member 't env)))
  12474. ;; (cond ((= x 1) 1) (t 2)
  12475. (lint-format "odd cond clause test: is t supposed to be #t? ~A" caller
  12476. (truncated-list->string clause))))
  12477. (if (never-false expr)
  12478. (if (not (= ctr len))
  12479. ;; (cond ((getenv s) x) ((= y z) w))
  12480. (lint-format "cond test ~A is never false: ~A" caller (car clause) (truncated-list->string form))
  12481. (if (not (or (memq expr '(#t else))
  12482. (side-effect? test env)))
  12483. (lint-format "cond last test could be #t: ~A" caller form)))
  12484. (if (never-true expr)
  12485. ;; (cond ((< 3 1) 2))
  12486. (lint-format "cond test ~A is never true: ~A" caller (car clause) (truncated-list->string form))))
  12487. (unless (side-effect? test env)
  12488. (cond ((or (memq test '(else #t))
  12489. (not (pair? sequel))
  12490. (pair? (cdr sequel))))
  12491. ((equal? test first-sequel)
  12492. ;; (cond ((= x 0) x) ((= x 1) (= x 1)))
  12493. (lint-format "no need to repeat the test: ~A" caller (lists->string clause (list test))))
  12494. ((and (pair? first-sequel)
  12495. (pair? (cdr first-sequel))
  12496. (null? (cddr first-sequel))
  12497. (equal? test (cadr first-sequel)))
  12498. (if (eq? (car first-sequel) 'not)
  12499. ;; (cond ((> x 2) (not (> x 2))))
  12500. (lint-format "perhaps replace ~A with #f" caller first-sequel)
  12501. ;; (cond (x (abs x)))
  12502. (lint-format "perhaps use => here: ~A" caller
  12503. (lists->string clause (list test '=> (car first-sequel))))))
  12504. ((and (eq? first-sequel #t)
  12505. (pair? test)
  12506. (not (memq (car test) '(or and)))
  12507. (eq? (return-type (car test) env) 'boolean?))
  12508. ;; (cond ((null? x) #t) (else y))
  12509. (lint-format "this #t could be omitted: ~A" caller (truncated-list->string clause))))
  12510. (if (member test exprs)
  12511. ;; (cond ((< x 2) 3) ((> x 0) 4) ((< x 2) 5))
  12512. (lint-format "cond test repeated: ~A" caller (truncated-list->string clause))
  12513. (set! exprs (cons test exprs))))
  12514. (if (boolean? expr)
  12515. (if (not expr)
  12516. ;; (cond ((< 3 1) 2))
  12517. (lint-format "cond test is always false: ~A" caller (truncated-list->string clause))
  12518. (if (not (= ctr len))
  12519. ;; (cond (#t 2) (x 3))
  12520. (lint-format "cond #t clause is not the last: ~A" caller (truncated-list->string form))))
  12521. (if (eq? test 'else)
  12522. (if (not (= ctr len))
  12523. ;; (cond (else 2) (x 3))
  12524. (lint-format "cond else clause is not the last: ~A" caller (truncated-list->string form)))
  12525. (lint-walk caller test env)))
  12526. (if (and (symbol? expr)
  12527. (not (var-member expr env))
  12528. (procedure? (symbol->value expr *e*)))
  12529. ;; (cond (< x 1) (else 1))
  12530. (lint-format "strange cond test: ~A in ~A is a procedure" caller expr clause))
  12531. (if (eq? result :unset)
  12532. (set! result sequel)
  12533. (if (not (equal? result sequel))
  12534. (set! result :unequal)))
  12535. (cond ((not (pair? sequel))
  12536. (if (not (null? sequel)) ; (not (null?...)) here is correct -- we're looking for stray dots
  12537. (lint-format "cond clause is messed up: ~A" caller (truncated-list->string clause))))
  12538. ((not (eq? first-sequel '=>))
  12539. (lint-walk-open-body caller 'cond sequel env))
  12540. ((or (not (pair? (cdr sequel)))
  12541. (pair? (cddr sequel)))
  12542. ;; (cond (x =>))
  12543. (lint-format "cond => target is messed up: ~A" caller (truncated-list->string clause)))
  12544. (else (let ((f (cadr sequel)))
  12545. (if (symbol? f)
  12546. (let ((val (symbol->value f *e*)))
  12547. (when (procedure? val)
  12548. (if (not (aritable? val 1)) ; here values might be in test expr
  12549. ;; (cond (x => expt))
  12550. (lint-format "=> target (~A) may be unhappy: ~A" caller f clause))
  12551. (let ((sig (procedure-signature val)))
  12552. (if (and (pair? sig)
  12553. (pair? (cdr sig)))
  12554. (let ((from-type (->lint-type expr))
  12555. (to-type (cadr sig)))
  12556. (if (not (or (memq from-type '(#f #t values))
  12557. (memq to-type '(#f #t values))
  12558. (any-compatible? to-type from-type)))
  12559. ;; (cond ((> x 0) => abs) (else y))
  12560. (lint-format "in ~A, ~A returns a ~A, but ~A expects ~A" caller
  12561. (truncated-list->string clause)
  12562. expr (prettify-checker-unq from-type)
  12563. f to-type)))))))
  12564. (if (and (pair? f)
  12565. (eq? (car f) 'lambda)
  12566. (pair? (cdr f))
  12567. (pair? (cadr f))
  12568. (not (= (length (cadr f)) 1)))
  12569. (lint-format "=> target (~A) may be unhappy: ~A" caller f clause)))
  12570. (lint-walk caller f env))))
  12571. (if (side-effect? expr env)
  12572. (begin
  12573. (set! falses ())
  12574. (set! trues ())
  12575. (set! result :unequal))
  12576. (begin
  12577. (if (not (member expr falses))
  12578. (set! falses (cons expr falses)))
  12579. (when (pair? expr)
  12580. (if (and (eq? (car expr) 'not)
  12581. (not (member (cadr expr) trues)))
  12582. (set! trues (cons (cadr expr) trues)))
  12583. (if (eq? (car expr) 'or)
  12584. (for-each (lambda (p)
  12585. (if (not (member p falses))
  12586. (set! falses (cons p falses))))
  12587. (cdr expr))))))))))
  12588. (cdr form))) ; for-each clause
  12589. (if has-else
  12590. (if (pair? result) ; all result clauses are the same (and not implicit)
  12591. ;; (cond (x #t) (else #t)) -> #t
  12592. (lint-format "perhaps ~A" caller (lists->string form
  12593. (if (null? (cdr result))
  12594. (car result)
  12595. `(begin ,@result)))))
  12596. (let* ((last-clause (and (> len 1)
  12597. (list-ref form len)))
  12598. (last-res (let ((clen (and (pair? last-clause)
  12599. (length last-clause))))
  12600. (and (integer? clen)
  12601. (> clen 1)
  12602. (list-ref last-clause (- clen 1))))))
  12603. (if (and (pair? last-res)
  12604. (memq (car last-res) '(#t else)))
  12605. ;; (cond (x y) (y z (else 3)))
  12606. (lint-format "perhaps cond else clause is misplaced: ~A in ~A" caller last-res last-clause))))
  12607. (when (and (= len 2)
  12608. (not (check-bool-cond caller form (cadr form) (caddr form) env))
  12609. (pair? (cadr form)) ; (cond 1 2)!
  12610. (pair? (caddr form)))
  12611. (let ((c1 (cadr form))
  12612. (c2 (caddr form)))
  12613. (if (equal? (simplify-boolean (car c1) () () env)
  12614. (simplify-boolean `(not ,(car c2)) () () env))
  12615. (lint-format "perhaps ~A" caller ; (cond ((x) y) ((not (x)) z)) -> (cond ((x) y) (else z))
  12616. (lists->string form `(cond ,c1 (else ,@(cdr c2)))))
  12617. (when (and (pair? (car c1)) ; (cond ((not x) y) (else z)) -> (cond (x z) (else y))
  12618. (pair? (cdr c1)) ; null case is handled elsewhere
  12619. (eq? (caar c1) 'not)
  12620. (memq (car c2) '(else #t)))
  12621. (let ((c1-len (tree-leaves (cdr c1))) ; try to avoid the dangling short case as in if
  12622. (c2-len (tree-leaves (cdr c2))))
  12623. (when (and (< (+ c1-len c2-len) 100)
  12624. (> (* c1-len 4) c2-len)) ; maybe 4 is too much
  12625. (lint-format "perhaps ~A" caller
  12626. (lists->string form
  12627. (if (or (pair? (cddr c1))
  12628. (pair? (cddr c2)))
  12629. `(cond (,(cadar c1) ,@(cdr c2)) (else ,@(cdr c1)))
  12630. `(if ,(cadar c1) ,(cadr c2) ,(cadr c1)))))))))))
  12631. (when has-combinations
  12632. (do ((new-clauses ())
  12633. (current-clauses ())
  12634. (clauses (cdr form) (cdr clauses)))
  12635. ((null? clauses)
  12636. (let ((len2 (= (length new-clauses) 2)))
  12637. (unless (and len2 ; i.e. don't go to check-bool-cond
  12638. (check-bool-cond caller form (cadr new-clauses) (car new-clauses) env))
  12639. ;; (cond ((= x 3) 3) ((= x 2) 4) ((= x 1) 4)) -> (case x ((3) 3) ((2 1) 4))
  12640. (lint-format "perhaps ~A" caller
  12641. (lists->string
  12642. form
  12643. (cond (all-eqv
  12644. (cond->case eqv-select (reverse new-clauses)))
  12645. ((not (and len2
  12646. (pair? (car new-clauses))
  12647. (memq (caar new-clauses) '(else #t))
  12648. (pair? (cadr new-clauses))
  12649. (pair? (caadr new-clauses))
  12650. (eq? (caaadr new-clauses) 'or)
  12651. (null? (cdadr new-clauses))))
  12652. `(cond ,@(reverse new-clauses)))
  12653. ((null? (cddar new-clauses)) ; (cond (A) (B) (else C)) -> (or A B C)
  12654. `(or ,@(cdaadr new-clauses) ,(cadar new-clauses)))
  12655. (else `(or ,@(cdaadr new-clauses) (begin ,@(cdar new-clauses))))))))
  12656. (set! simplifications ())
  12657. (set! all-eqv #f)))
  12658. (let* ((clause (car clauses))
  12659. (result (cdr clause))) ; can be null in which case the test is the result
  12660. (cond ((and (pair? simplifications)
  12661. (assq clause simplifications))
  12662. => (lambda (e)
  12663. (set! clause (cons (cdr e) result)))))
  12664. (if (and (pair? (cdr clauses))
  12665. (equal? result (cdadr clauses)))
  12666. (set! current-clauses (cons clause current-clauses))
  12667. (if (pair? current-clauses)
  12668. (begin
  12669. (set! current-clauses (cons clause current-clauses))
  12670. (set! new-clauses (cons
  12671. (cons (simplify-boolean `(or ,@(map car (reverse current-clauses))) () () env)
  12672. result)
  12673. new-clauses))
  12674. (set! current-clauses ()))
  12675. (set! new-clauses (cons clause new-clauses)))))))
  12676. (when (and all-eqv
  12677. (> len (if has-else 2 1))) ; (cond (x y)) -- kinda dumb, but (if x y) isn't much shorter
  12678. ;; (cond ((= x 0) x) ((= x 1) (= x 1))) -> (case x ((0) x) ((1) (= x 1)))
  12679. (lint-format "perhaps use case instead of cond: ~A" caller
  12680. (lists->string form (cond->case eqv-select (cdr form)))))
  12681. (if (and (= len 2)
  12682. has-else
  12683. (null? (cdadr form)))
  12684. (let ((else-clause (if (null? (cddr (caddr form)))
  12685. (cadr (caddr form))
  12686. `(begin ,@(cdr (caddr form))))))
  12687. ;; (cond ((a)) (else A)) -> (or (a) A)
  12688. (lint-format "perhaps ~A" caller (lists->string form `(or ,(caadr form) ,else-clause)))))
  12689. ;; --------
  12690. (unless (or has-combinations all-eqv)
  12691. ;; look for repeated ((op x c1) c2) -> ((assoc x '((c1 . c2)...)) => cdr) anywhere in the clause list
  12692. (let ((nc ())
  12693. (op #f)
  12694. (sym-access #f)
  12695. (start #f)
  12696. (changed #f))
  12697. ;; extending this to memx possibilities got only 1 hit and involved ca. 20 lines
  12698. (define (car-with-expr cls)
  12699. (cond ((and (pair? simplifications)
  12700. (assq cls simplifications))
  12701. => (lambda (e)
  12702. (set! changed #t)
  12703. (cons (cdr e) (cdr cls))))
  12704. (else cls)))
  12705. (define (start-search clauses test)
  12706. (if (code-constant? (cadr test))
  12707. (if (memq (car test) '(= string=? string-ci=? eq? eqv? equal? char=? char-ci=?))
  12708. (set! sym-access caddr))
  12709. (if (code-constant? (caddr test))
  12710. (set! sym-access cadr)))
  12711. (if sym-access
  12712. (begin
  12713. (set! start clauses)
  12714. (set! op (car test)))
  12715. (set! nc (cons (car-with-expr (car clauses)) nc))))
  12716. (do ((clauses (cdr form) (cdr clauses)))
  12717. ((or (null? clauses)
  12718. (not (pair? (car clauses))))
  12719. (if (and changed
  12720. (null? clauses))
  12721. ;; (cond ((< x 2) 3) ((> x 0) 4) ((< x 2) 5)) -> (cond ((< x 2) 3) ((> x 0) 4))
  12722. (lint-format "perhaps ~A" caller
  12723. (lists->string form `(cond ,@(reverse (map (lambda (c)
  12724. (if (not (car c)) (values) c))
  12725. nc)))))))
  12726. (let ((test (caar clauses)))
  12727. (let ((ok-but-at-end #f)
  12728. (looks-ok (let ((result (cdar clauses)))
  12729. (and (pair? test)
  12730. (pair? (cdr test))
  12731. (pair? (cddr test))
  12732. (null? (cdddr test))
  12733. (pair? result)
  12734. (null? (cdr result))
  12735. (not (symbol? (car result)))
  12736. (or (not (pair? (car result))) ; quoted lists look bad in this context
  12737. (and (eq? (caar result) 'quote)
  12738. (not (pair? (cadar result)))))))))
  12739. (if (not start)
  12740. (if (and looks-ok
  12741. (not (null? (cdr clauses))))
  12742. (start-search clauses test)
  12743. (set! nc (cons (car-with-expr (car clauses)) nc)))
  12744. (unless (and looks-ok
  12745. (eq? (car test) op)
  12746. (equal? (sym-access test) (sym-access (caar start)))
  12747. (code-constant? ((if (eq? sym-access cadr) caddr cadr) test))
  12748. (not (set! ok-but-at-end (null? (cdr clauses)))))
  12749. (if (eq? (cdr start) clauses) ; only one item in the block, or two but it's just two at the end
  12750. (begin
  12751. (set! nc (cons (car start) nc))
  12752. (if (and looks-ok
  12753. (not (null? (cdr clauses))))
  12754. (start-search clauses test)
  12755. (begin
  12756. (set! start #f)
  12757. (set! nc (cons (car-with-expr (car clauses)) nc)))))
  12758. ;; multiple hits -- can we combine them?
  12759. (let ((alist ())
  12760. (cc (if (eq? sym-access cadr) caddr cadr)))
  12761. (set! changed #t)
  12762. (do ((sc start (cdr sc)))
  12763. ((if ok-but-at-end
  12764. (null? sc)
  12765. (eq? sc clauses))
  12766. (case op
  12767. ((eq?)
  12768. (set! nc (cons `((assq ,(sym-access (caar start)) ',(reverse alist)) => cdr) nc)))
  12769. ((eqv? char=?)
  12770. (set! nc (cons `((assv ,(sym-access (caar start)) ',(reverse alist)) => cdr) nc)))
  12771. ((equal?)
  12772. (set! nc (cons `((assoc ,(sym-access (caar start)) ',(reverse alist)) => cdr) nc)))
  12773. ((string=?)
  12774. ;; this is probably faster than assoc + string=?, but it creates symbols
  12775. (let ((nlst (map (lambda (c)
  12776. (cons (string->symbol (car c)) (cdr c)))
  12777. alist)))
  12778. (set! nc (cons `((assq (string->symbol ,(sym-access (caar start))) ',(reverse nlst)) => cdr) nc))))
  12779. (else
  12780. (set! nc (cons `((assoc ,(sym-access (caar start)) ',(reverse alist) ,op) => cdr) nc)))))
  12781. (set! alist (cons (cons (unquoted (cc (caar sc))) (unquoted (cadar sc))) alist)))
  12782. (if (and looks-ok
  12783. (not (null? (cdr clauses))))
  12784. (start-search clauses test)
  12785. (begin
  12786. (set! start #f)
  12787. (if (not ok-but-at-end)
  12788. (set! nc (cons (car-with-expr (car clauses)) nc))))))))))))))
  12789. ;; look for case at end (case in the middle is tricky due to #f handling)
  12790. (when (and (> len 3)
  12791. (= suggest made-suggestion))
  12792. (let ((rform (reverse form))
  12793. (eqv-select #f)
  12794. (elen (if has-else (- len 1) len)))
  12795. (if has-else (set! rform (cdr rform)))
  12796. (set! eqv-select (eqv-selector (caar rform)))
  12797. (when eqv-select
  12798. (do ((clauses rform (cdr clauses))
  12799. (ctr 0 (+ ctr 1)))
  12800. ((or (null? clauses)
  12801. (let ((clause (car clauses)))
  12802. (or (and (pair? (cdr clause))
  12803. (eq? (cadr clause) '=>)) ; case sends selector, but cond sends test result
  12804. (not (cond-eqv? (car clause) eqv-select #t)))))
  12805. (when (and (pair? clauses)
  12806. (> ctr 1))
  12807. ;; (cond ((pair? x) 3) ((eq? x 'a) z) ((eq? x 'b) (* 2 z)) ((eq? x 'c)... ->
  12808. ;; (if (pair? x) 3 (case x ((a) z) ((b) (* 2 z)) ((c) (display z))))
  12809. (lint-format "possibly use case at the end: ~A" caller
  12810. (lists->string form
  12811. (let ((else-case (cond->case eqv-select ; cond->case will handle the else branch
  12812. (list-tail (cdr form) (- elen ctr)))))
  12813. (if (= (- elen ctr) 1)
  12814. (if (equal? (cdadr form) '(#f))
  12815. `(and (not ,(caadr form)) ,else-case)
  12816. `(if ,@(cadr form) ,else-case))
  12817. `(cond ,@(copy (cdr form) (make-list (- elen ctr)))
  12818. (else ,else-case))))))))))))
  12819. ;; --------
  12820. ;; repeated test exprs handled once
  12821. (let ((exprs ())
  12822. (reps ())
  12823. (ctr 0)
  12824. (pos 0)
  12825. (head-len 0)
  12826. (else-leaves 0)
  12827. (else-result #f))
  12828. (for-each (lambda (c)
  12829. (set! pos (+ pos 1))
  12830. (cond ((and (pair? c)
  12831. (memq (car c) '(#t else)))
  12832. (set! else-result (cdr c))
  12833. (set! else-leaves (tree-leaves else-result)))
  12834. ((not (and (pair? c)
  12835. (pair? (car c))
  12836. (or (eq? (caar c) 'and)
  12837. (member (car c) reps))))
  12838. (set! exprs ())
  12839. (set! reps ())
  12840. (set! ctr 0))
  12841. ((null? exprs)
  12842. (set! head-len pos)
  12843. (set! exprs (cdar c))
  12844. (set! reps exprs)
  12845. (set! ctr 1))
  12846. (else
  12847. (set! ctr (+ ctr 1))
  12848. (set! reps (remove-if (lambda (rc)
  12849. (not (or (equal? rc (car c))
  12850. (member rc (cdar c)))))
  12851. reps)))))
  12852. (cdr form))
  12853. (when (and (pair? reps)
  12854. (> ctr 1)
  12855. (< else-leaves (* ctr (length reps) 3)))
  12856. ;; (cond ((pair? z) 32) ((and (pair? x) (pair? w)) 12) ((pair? x) 2) (else 0)) ->
  12857. ;; (cond ((pair? z) 32) ((not (pair? x)) 0) ((pair? w) 12) (else 2))
  12858. (lint-format "perhaps ~A" caller
  12859. (lists->string form
  12860. (let ((not-reps
  12861. (simplify-boolean (if (null? (cdr reps))
  12862. `(not ,(car reps))
  12863. `(not (and ,@reps)))
  12864. () () env)))
  12865. `(,@(copy form (make-list head-len))
  12866. (,not-reps
  12867. ,@(or else-result '(#<unspecified>)))
  12868. ,@(let mapper ((clauses (list-tail form head-len))
  12869. (lst ()))
  12870. (if (null? clauses)
  12871. (reverse lst)
  12872. (let ((new-clause
  12873. (let ((c (car clauses)))
  12874. (if (memq (car c) '(else #t))
  12875. c
  12876. `(,(if (member (car c) reps)
  12877. 'else
  12878. (remove-if (lambda (rc)
  12879. (member rc reps))
  12880. (car c)))
  12881. ,@(cdr c))))))
  12882. (if (and (pair? new-clause)
  12883. (pair? (car new-clause))
  12884. (eq? (caar new-clause) 'and)
  12885. (pair? (cdar new-clause))
  12886. (null? (cddar new-clause)))
  12887. (set-car! new-clause (cadar new-clause)))
  12888. (if (memq (car new-clause) '(else #t))
  12889. (reverse (cons new-clause lst))
  12890. (mapper (cdr clauses) (cons new-clause lst))))))))))))
  12891. (when (pair? (cadr form))
  12892. (if (= len 1)
  12893. (let ((clause (cadr form))) ; (cond (a)) -> a, (cond (a b)) -> (if a b) etc
  12894. (if (null? (cdr clause))
  12895. (lint-format "perhaps ~A" caller (lists->string form (car clause)))
  12896. (if (and (not (eq? (cadr clause) '=>))
  12897. (or (pair? (cddr clause))
  12898. (= suggest made-suggestion)))
  12899. ;; (cond ((= x 1) 32)) -> (if (= x 1) 32)
  12900. (lint-format "perhaps ~A" caller
  12901. (lists->string form
  12902. (if (null? (cddr clause))
  12903. `(if ,(car clause) ,(cadr clause))
  12904. (if (and (pair? (car clause))
  12905. (eq? (caar clause) 'not))
  12906. `(unless ,@(cdar clause) ,@(cdr clause))
  12907. `(when ,(car clause) ,@(cdr clause)))))))))
  12908. (when has-else ; len > 1 here
  12909. (let ((last-clause (list-ref form (- len 1)))) ; not the else branch! -- just before it.
  12910. (when (and (= suggest made-suggestion) ; look for all results the same
  12911. (pair? (cadr form))
  12912. (pair? (cdadr form)))
  12913. (let ((result (list-ref (cadr form) (- (length (cadr form)) 1)))
  12914. (else-clause (cdr (list-ref form len))))
  12915. (when (every? (lambda (c)
  12916. (and (pair? c)
  12917. (pair? (cdr c))
  12918. (equal? result (list-ref c (- (length c) 1)))))
  12919. (cddr form))
  12920. ;; (cond ((and (display x) x) 32) (#t 32)) -> (begin (and (display x) x) 32)
  12921. (lint-format "perhaps ~A" caller
  12922. (lists->string form
  12923. (if (= len 2) ; one is else -- this case is very common
  12924. (let* ((c1-len (length (cdr last-clause)))
  12925. (new-c1 (case c1-len
  12926. ((1) #f)
  12927. ((2) (cadr last-clause))
  12928. (else `(begin ,@(copy (cdr last-clause) (make-list (- c1-len 1)))))))
  12929. (else-len (length else-clause))
  12930. (new-else (case else-len
  12931. ((1) #f)
  12932. ((2) (car else-clause))
  12933. (else `(begin ,@(copy else-clause (make-list (- else-len 1))))))))
  12934. `(begin
  12935. ,(if (= c1-len 1)
  12936. (if new-else
  12937. `(if (not ,(car last-clause)) ,new-else)
  12938. (car last-clause))
  12939. (if (= else-len 1)
  12940. (if new-c1
  12941. `(if ,(car last-clause) ,new-c1)
  12942. (car last-clause))
  12943. `(if ,(car last-clause) ,new-c1 ,new-else)))
  12944. ,result))
  12945. `(begin ; this almost never happens
  12946. (cond ,@(map (lambda (c)
  12947. (let ((len (length c)))
  12948. (if (= len 2)
  12949. (if (or (memq (car c) '(else #t))
  12950. (not (side-effect? (car c) env)))
  12951. (values)
  12952. (car c))
  12953. (copy c (make-list (- len 1))))))
  12954. (cdr form)))
  12955. ,result)))))))
  12956. ;; a few dozen hits here
  12957. ;; the 'case parallel gets 2 hits, complex selectors
  12958. ;; len = (- (length form) 1) = number of clauses
  12959. (when (and (> len 2)
  12960. (or (null? (cdr last-clause))
  12961. (and (pair? (cdr last-clause))
  12962. (null? (cddr last-clause))
  12963. (boolean? (cadr last-clause)))))
  12964. (let ((else-clause (cdr (list-ref form len)))
  12965. (next-clause (cdr (list-ref form (- len 2)))))
  12966. (when (and (pair? else-clause)
  12967. (null? (cdr else-clause))
  12968. (boolean? (car else-clause))
  12969. (not (equal? (cdr last-clause) else-clause))
  12970. (pair? next-clause)
  12971. (null? (cdr next-clause))
  12972. (not (boolean? (car next-clause))))
  12973. (lint-format "perhaps ~A" caller
  12974. (lists->string form
  12975. `(,@(copy form (make-list (- len 1)))
  12976. (else ,(if (car else-clause)
  12977. `(not ,(car last-clause))
  12978. (car last-clause)))))))))
  12979. ;; (cond ((= x y) 2) ((= x 2) #f) (else #t)) -> (cond ((= x y) 2) (else (not (= x 2))))
  12980. ;; (cond ((= x y) 2) ((= x 2) #t) (else #f)) -> (cond ((= x y) 2) (else (= x 2)))
  12981. (when (= len 3)
  12982. (let ((first-clause (cadr form))
  12983. (else-clause (cdr (list-ref form len))))
  12984. (when (and (or (null? (cdr first-clause))
  12985. (and (null? (cddr first-clause))
  12986. (boolean? (cadr first-clause))))
  12987. (pair? last-clause)
  12988. (or (null? (cdr last-clause))
  12989. (null? (cddr last-clause))))
  12990. (if (and (pair? (cdr first-clause))
  12991. (not (cadr first-clause)) ; (cond (A #f) (B #t) (else C)) -> (and (not A) (or B C))
  12992. (or (null? (cdr last-clause))
  12993. (eq? (cadr last-clause) #t)))
  12994. (lint-format "perhaps ~A" caller
  12995. (lists->string form
  12996. (simplify-boolean
  12997. `(and (not ,(car first-clause))
  12998. (or ,(car last-clause)
  12999. ,@(if (null? (cdr else-clause))
  13000. else-clause
  13001. `(begin ,@else-clause))))
  13002. () () env)))
  13003. (if (and (or (null? (cdr first-clause)) ; (cond (A #t) (B C) (else #f)) -> (or A (and B C))
  13004. (eq? (cadr first-clause) #t))
  13005. (not (car else-clause))
  13006. (null? (cdr else-clause)))
  13007. (lint-format "perhaps ~A" caller
  13008. (lists->string form
  13009. `(or ,(car first-clause)
  13010. (and ,@last-clause)))))))
  13011. (when (and (equal? (cdr first-clause) else-clause) ; a = else result
  13012. (pair? (cdr last-clause)) ; b does exist
  13013. (not (eq? (cadr last-clause) '=>))) ; no => in b
  13014. ;; (cond (A a) (B b) (else a)) -> (if (or A (not B)) a b)
  13015. (lint-format "perhaps ~A" caller
  13016. (lists->string form
  13017. (let ((A (car first-clause))
  13018. (a (cdr first-clause))
  13019. (B (car last-clause))
  13020. (b (cdr last-clause)))
  13021. (let ((nexpr (simplify-boolean `(or ,A (not ,B)) () () env)))
  13022. (cond ((not (and (null? (cdr a))
  13023. (null? (cdr b))))
  13024. `(cond (,nexpr ,@a) (else ,@b)))
  13025. ((eq? (car a) #t)
  13026. (if (not (car b))
  13027. nexpr
  13028. (simplify-boolean `(or ,nexpr ,(car b)) () () env)))
  13029. ((car a) ; i.e a is not #f
  13030. `(if ,nexpr ,(car a) ,(car b)))
  13031. ((eq? (car b) #t)
  13032. (simplify-boolean `(not ,nexpr) () () env))
  13033. (else (simplify-boolean `(and (not ,nexpr) ,(car b)) () () env))))))))))
  13034. (when (> len 3)
  13035. ;; this is not ideal
  13036. (let ((e (list-ref form len)) ; (cond (W X) (A B) (C D) (else B)) -> (cond (W X) ((or A (not C)) B) (else D))
  13037. (b (list-ref form (- len 1)))
  13038. (a (list-ref form (- len 2))))
  13039. (if (and (pair? a)
  13040. (pair? (cdr a)) ; is (else) a legal cond clause? -- yes, it returns else...
  13041. (pair? e)
  13042. (equal? (cdr a) (cdr e))
  13043. (pair? b)
  13044. (pair? (cdr b))
  13045. (not (eq? (cadr b) '=>)))
  13046. (let ((expr (simplify-boolean `(or ,(car a) (not ,(car b))) () () env)))
  13047. (lint-format "perhaps ~A" caller
  13048. (lists->string form `(cond ,(if (> len 4) '... (cadr form))
  13049. (,expr ,@(cdr a))
  13050. (else ,@(cdr b)))))))))
  13051. (let ((arg1 (cadr form))
  13052. (arg2 (caddr form)))
  13053. (when (and (pair? arg1)
  13054. (pair? (car arg1))
  13055. (pair? (cdr arg1))
  13056. (pair? arg2)
  13057. (eq? (caar arg1) 'and)
  13058. (null? (cddr arg1))
  13059. (pair? (cdr arg2))
  13060. (null? (cddr arg2))
  13061. (member (car arg2) (cdar arg1))
  13062. (= (length (cdar arg1)) 2))
  13063. ;; (cond ((and A B) c) (B d) (else e)) -> (cond (B (if A c d)) (else e))
  13064. (lint-format "perhaps ~A" caller
  13065. (lists->string form
  13066. `(cond (,(car arg2)
  13067. (if ,((if (equal? (car arg2) (cadar arg1)) caddar cadar) arg1)
  13068. ,(cadr arg1)
  13069. ,(cadr arg2)))
  13070. ,@(cdddr form))))))
  13071. (if (and (pair? last-clause) ; (cond ... ((or ...)) (else ...)) -> (cond ... (else (or ... ...)))
  13072. (pair? (car last-clause))
  13073. (null? (cdr last-clause))
  13074. (eq? (caar last-clause) 'or))
  13075. (let ((else-clause (let ((e (cdr (list-ref form len))))
  13076. (if (null? (cdr e))
  13077. (car e)
  13078. `(begin ,@e)))))
  13079. ;; (cond ((A) B) ((or C D)) (else E)) -> (cond ((A) B) (else (or C D E)))
  13080. (lint-format "perhaps ~A" caller
  13081. (lists->string form
  13082. `(cond ,@(copy (cdr form) (make-list (- len 2)))
  13083. (else (or ,@(cdar last-clause) ,else-clause))))))))))
  13084. (let ((last-clause (list-ref form (if has-else (- len 1) len)))) ; not the else branch! -- just before it.
  13085. (if (and (pair? last-clause) ; (cond ... (A (cond ...)) (else B)) -> (cond ... ((not A) B) ...)
  13086. (pair? (cdr last-clause))
  13087. (null? (cddr last-clause))
  13088. (pair? (cadr last-clause))
  13089. (memq (caadr last-clause) '(if cond)))
  13090. (let ((new-test (simplify-boolean `(not ,(car last-clause)) () () env))
  13091. (new-result (if has-else
  13092. (cdr (list-ref form len))
  13093. (if (eq? form lint-mid-form)
  13094. ()
  13095. (list #<unspecified>)))))
  13096. (if (eq? (caadr last-clause) 'cond)
  13097. ;; (cond (A (cond (B c) (else D))) (else E)) -> (cond ((not A) E) (B c) (else D))
  13098. (lint-format "perhaps ~A" caller
  13099. (lists->string form
  13100. `(cond ,@(copy (cdr form) (make-list (- len (if has-else 2 1))))
  13101. (,new-test ,@new-result)
  13102. ,@(cdadr last-clause))))
  13103. (if (= (length (cadr last-clause)) 4)
  13104. (let ((if-form (cdadr last-clause)))
  13105. ;; (cond (A B) (C (if D d E)) (else F)) -> (cond (A B) ((not C) F) (D d) (else E))
  13106. (lint-format "perhaps ~A" caller
  13107. (lists->string form
  13108. `(cond ,@(copy (cdr form) (make-list (- len (if has-else 2 1))))
  13109. (,new-test ,@new-result)
  13110. (,(car if-form) ,@(unbegin (cadr if-form)))
  13111. (else ,@(unbegin (caddr if-form))))))))))
  13112. (when (> len 2) ; rewrite nested conds as one cond
  13113. (let ((lim (if has-else (- len 2) len))
  13114. (tlen (tree-leaves form)))
  13115. (when (< tlen 200)
  13116. (set! tlen (/ tlen 4))
  13117. (do ((i 0 (+ i 1))
  13118. (k (+ lim 1) (- k 1))
  13119. (p (cdr form) (cdr p)))
  13120. ((or (not (pair? p))
  13121. (= i lim)))
  13122. (let ((nc (car p)))
  13123. (if (and (pair? nc)
  13124. (pair? (cdr nc))
  13125. (null? (cddr nc))
  13126. (pair? (cadr nc))
  13127. (eq? (caadr nc) 'cond)
  13128. (>= (length (cdadr nc)) (* 2 k))
  13129. (> (tree-leaves nc) tlen))
  13130. (let ((new-test (simplify-boolean `(not ,(car nc)) () () env))
  13131. (new-result (if (and has-else
  13132. (= i (- lim 1))
  13133. (null? (cddadr p))
  13134. (null? (cddr (caddr p))))
  13135. `(if ,(caadr p) ,(cadadr p) ,(cadr (caddr p)))
  13136. `(cond ,@(cdr p)))))
  13137. ;; (cond ((= x 0) 1) ((= x 3) (cond ((not y) 1) ((pair? y) 2) ((eq? y 'a) 3) (else 4))) ((< x 200) 2) (else 5)) ->
  13138. ;; (cond ((= x 0) 1) ((not (= x 3)) (if (< x 200) 2 5)) ((not y) 1) ((pair? y) 2) ((eq? y 'a) 3) (else 4))
  13139. (lint-format "perhaps ~A" caller
  13140. (lists->string form
  13141. `(cond ,@(copy (cdr form) (make-list i))
  13142. (,new-test ,new-result)
  13143. ,@(cdadr nc))))))))))))))))
  13144. env))
  13145. (hash-table-set! h 'cond cond-walker))
  13146. ;; ---------------- case ----------------
  13147. (let ()
  13148. (define case-walker
  13149. (let ((selector-types '(#t symbol? char? boolean? integer? rational? real? complex? number? null? eof-object?)))
  13150. (lambda (caller form env)
  13151. ;; here the keys are not evaluated, so we might have a list like (letrec define ...)
  13152. ;; also unlike cond, only 'else marks a default branch (not #t)
  13153. (if (< (length form) 3)
  13154. ;; (case 3)
  13155. (lint-format "case is messed up: ~A" caller (truncated-list->string form))
  13156. (let ((sel-type #t)
  13157. (selector (cadr form))
  13158. (suggest made-suggestion))
  13159. ;; ----------------
  13160. ;; if regular case + else -- just like cond above
  13161. (let ((len (- (length form) 2))) ; number of clauses
  13162. (when (and (> len 1) ; (case x (else ...)) is handled elsewhere
  13163. (pair? (cdr form))
  13164. (pair? (cddr form))
  13165. (pair? (caddr form))
  13166. (not (tree-set-member '(unquote #_{list}) form)))
  13167. (let ((first-clause (caddr form))
  13168. (else-clause (list-ref form (+ len 1))))
  13169. (when (and (pair? else-clause)
  13170. (eq? (car else-clause) 'else)
  13171. (pair? (cdr first-clause))
  13172. (pair? (cadr first-clause))
  13173. (not (hash-table-ref syntaces (caadr first-clause)))
  13174. (pair? (cdadr first-clause))
  13175. (null? (cddr first-clause))
  13176. (every? (lambda (c)
  13177. (and (pair? c)
  13178. (pair? (cdr c))
  13179. (pair? (cadr c))
  13180. (null? (cddr c))
  13181. (not (hash-table-ref syntaces (caadr c)))
  13182. (equal? (cdadr first-clause) (cdadr c))))
  13183. (cdddr form)))
  13184. ;; (case x ((a) (f y z)) (else (g y z))) -> ((if (eq? x 'a) f g) y z)
  13185. (lint-format "perhaps ~A" caller ; all results share trailing args
  13186. (lists->string form
  13187. (if (and (= len 2)
  13188. (symbol? (caar first-clause))
  13189. (null? (cdar first-clause)))
  13190. `((if (eq? ,(cadr form) ',(caar first-clause))
  13191. ,(caadr first-clause)
  13192. ,(caadr else-clause))
  13193. ,@(cdadr first-clause))
  13194. `((case ,(cadr form)
  13195. ,@(map (lambda (c)
  13196. (list (car c) (caadr c)))
  13197. (cddr form)))
  13198. ,@(cdadr first-clause))))))
  13199. (when (and (pair? (cdr first-clause))
  13200. (null? (cddr first-clause))
  13201. (pair? (cadr first-clause))
  13202. (pair? else-clause)
  13203. (eq? (car else-clause) 'else)
  13204. (pair? (cdr else-clause))
  13205. (pair? (cadr else-clause))
  13206. (or (equal? (caadr first-clause) (caadr else-clause)) ; there's some hope we'll match
  13207. (escape? (cadr else-clause) env)))
  13208. (let ((first-result (cadr first-clause))
  13209. (first-func (caadr first-clause))
  13210. (else-error (escape? (cadr else-clause) env)))
  13211. (when (and (pair? (cdr first-result))
  13212. (not (eq? first-func 'values))
  13213. (or (not (hash-table-ref syntaces first-func))
  13214. (eq? first-func 'set!))
  13215. (every? (lambda (c)
  13216. (and (pair? c)
  13217. (pair? (cdr c))
  13218. (pair? (cadr c))
  13219. (null? (cddr c))
  13220. (pair? (cdadr c))
  13221. (or (equal? first-func (caadr c))
  13222. (and (eq? c else-clause)
  13223. else-error))))
  13224. (cdddr form)))
  13225. ((lambda (header-len trailer-len result-mid-len)
  13226. (when (and (or (not (eq? first-func 'set!))
  13227. (> header-len 1))
  13228. (or (not (eq? first-func '/))
  13229. (> header-len 1)
  13230. (> trailer-len 0)))
  13231. (let ((header (copy first-result (make-list header-len)))
  13232. (trailer (copy first-result (make-list trailer-len) (- (length first-result) trailer-len))))
  13233. (if (= len 2)
  13234. (unless (equal? first-result (cadr else-clause)) ; handled elsewhere (all results equal -> result)
  13235. ;; (case x ((1) (+ x 1)) (else (+ x 3))) -> (+ x (if (eqv? x 1) 1 3))
  13236. (lint-format "perhaps ~A" caller
  13237. (let ((else-result (cadr else-clause)))
  13238. (let ((first-mid-len (- (length first-result) header-len trailer-len))
  13239. (else-mid-len (- (length else-result) header-len trailer-len)))
  13240. (let* ((fmid (if (= first-mid-len 1)
  13241. (list-ref first-result header-len)
  13242. `(values ,@(copy first-result (make-list first-mid-len) header-len))))
  13243. (emid (if else-error
  13244. else-result
  13245. (if (= else-mid-len 1)
  13246. (list-ref else-result header-len)
  13247. `(values ,@(copy else-result (make-list else-mid-len) header-len)))))
  13248. (middle (if (= (length (car first-clause)) 1)
  13249. `(eqv? ,(cadr form) ,(caar first-clause))
  13250. `(memv ,(cadr form) ',(car first-clause)))))
  13251. (lists->string form `(,@header (if ,middle ,fmid ,emid) ,@trailer)))))))
  13252. ;; len > 2 so use case in the revision
  13253. (let ((middle (map (lambda (c)
  13254. (if (and else-error
  13255. (eq? c else-clause))
  13256. else-clause
  13257. (let ((test (car c))
  13258. (result (cadr c)))
  13259. (let ((mid-len (- (length result) header-len trailer-len)))
  13260. `(,test ,(if (= mid-len 1)
  13261. (list-ref result header-len)
  13262. `(values ,@(copy result (make-list mid-len) header-len))))))))
  13263. (cddr form))))
  13264. ;; (case x ((0) (log x 2)) ((1) (log x 3)) (else (error 'oops))) -> (log x (case x ((0) 2) ((1) 3) (else (error 'oops))))
  13265. (lint-format "perhaps ~A" caller
  13266. (lists->string form `(,@header (case ,(cadr form) ,@middle) ,@trailer))))))))
  13267. (partition-form (cddr form) (if else-error (- len 1) len)))))))))
  13268. ;; ----------------
  13269. (if (every? (lambda (c) ; (case x ((a) a) ((b) b)) -> (symbol->value x)
  13270. (and (pair? c)
  13271. (pair? (car c))
  13272. (symbol? (caar c))
  13273. (null? (cdar c))
  13274. (pair? (cdr c))
  13275. (null? (cddr c))
  13276. (eq? (caar c) (cadr c)))) ; the quoted case happens only in test suites
  13277. (cddr form))
  13278. (lint-format "perhaps (ignoring the unmatched case) ~A" caller (lists->string form `(symbol->value ,(cadr form)))))
  13279. (when (= suggest made-suggestion)
  13280. (let ((clauses (cddr form))) ; (case x ((a) #t) (else #f)) -> (eq? x 'a) -- this stuff actually happens!
  13281. (if (null? (cdr clauses))
  13282. (let ((clause (car clauses)))
  13283. (when (and (pair? clause)
  13284. (pair? (car clause))
  13285. (pair? (cdr clause)))
  13286. (let ((keys (car clause)))
  13287. ;; (case 3 ((0) #t)) -> (if (eqv? 3 0) #t)
  13288. ;; (case x ((#(0)) 2)) -> (if (eqv? x #(0)) 2)
  13289. (lint-format "perhaps ~A" caller
  13290. (lists->string form
  13291. (let ((test (cond ((pair? (cdr keys))
  13292. `(memv ,(cadr form) ',keys))
  13293. ((and (symbol? (car keys))
  13294. (not (keyword? (car keys))))
  13295. `(eq? ,(cadr form) ',(car keys)))
  13296. ((or (keyword? (car keys))
  13297. (null? (car keys)))
  13298. `(eq? ,(cadr form) ,(car keys)))
  13299. ((not (boolean? (car keys)))
  13300. `(eqv? ,(cadr form) ,(car keys)))
  13301. ((car keys)
  13302. (cadr form))
  13303. (else `(not ,(cadr form)))))
  13304. (op (if (and (pair? (cdr clause))
  13305. (pair? (cddr clause)))
  13306. 'when 'if)))
  13307. `(,op ,test ,@(cdr clause))))))))
  13308. (when (and (null? (cddr clauses))
  13309. (pair? (car clauses))
  13310. (pair? (cadr clauses))
  13311. (eq? (caadr clauses) 'else)
  13312. (pair? (cdar clauses))
  13313. (pair? (cdadr clauses))
  13314. (null? (cddar clauses))
  13315. (null? (cddadr clauses))
  13316. (not (equal? (cadadr clauses) (cadar clauses))))
  13317. (let* ((akey (null? (cdaar clauses)))
  13318. (keylist ((if akey caaar caar) clauses))
  13319. (quoted (or (not akey) (symbol? keylist)))
  13320. (op (if (every? symbol? (caar clauses))
  13321. (if akey 'eq? 'memq)
  13322. (if akey 'eqv? 'memv))))
  13323. ;; can't use '= or 'char=? here because the selector may return anything
  13324. ;; (case x ((#\a) 3) (else 4)) -> (if (eqv? x #\a) 3 4)
  13325. ;; (case x ((a) #t) (else #f)) -> (eq? x 'a)
  13326. (lint-format "perhaps ~A" caller
  13327. (lists->string form
  13328. (cond ((and (boolean? (cadar clauses))
  13329. (boolean? (cadadr clauses)))
  13330. (if (cadadr clauses)
  13331. (if quoted
  13332. `(not (,op ,selector ',keylist))
  13333. `(not (,op ,selector ,keylist)))
  13334. (if quoted
  13335. `(,op ,selector ',keylist)
  13336. `(,op ,selector ,keylist))))
  13337. ((not (cadadr clauses)) ; (else #f) happens a few times
  13338. (simplify-boolean
  13339. (if quoted
  13340. `(and (,op ,selector ',keylist) ,(cadar clauses))
  13341. `(and (,op ,selector ,keylist) ,(cadar clauses)))
  13342. () () env))
  13343. (quoted
  13344. `(if (,op ,selector ',keylist) ,(cadar clauses) ,(cadadr clauses)))
  13345. (else
  13346. (let ((select-expr (if (and (eq? op 'eqv?)
  13347. (boolean? keylist)
  13348. (or (and (symbol? selector)
  13349. (not keylist))
  13350. (and (pair? selector)
  13351. (symbol? (car selector))
  13352. (let ((sig (arg-signature (car selector) env)))
  13353. (and (pair? sig)
  13354. (eq? (car sig) 'boolean?))))))
  13355. (if keylist selector `(not ,selector))
  13356. `(,op ,selector ,keylist))))
  13357. `(if ,select-expr ,(cadar clauses) ,(cadadr clauses))))))))))))
  13358. (if (and (not (pair? selector))
  13359. (constant? selector))
  13360. ;; (case 3 ((0) #t))
  13361. (lint-format "case selector is a constant: ~A" caller (truncated-list->string form)))
  13362. (if (symbol? selector)
  13363. (set-ref selector caller form env)
  13364. (lint-walk caller selector env))
  13365. (if (and (pair? selector)
  13366. (symbol? (car selector)))
  13367. (begin
  13368. (set! sel-type (return-type (car selector) env))
  13369. (if (and (symbol? sel-type)
  13370. (not (memq sel-type selector-types)))
  13371. ;; (case (list 1) ((0) #t))
  13372. (lint-format "case selector may not work with eqv: ~A" caller (truncated-list->string selector)))))
  13373. (let ((all-keys ())
  13374. (all-exprs ())
  13375. (ctr 0)
  13376. (result :unset)
  13377. (exprs-repeated #f)
  13378. (else-foldable #f)
  13379. (has-else #f)
  13380. (len (length (cddr form))))
  13381. (for-each
  13382. (lambda (clause)
  13383. (set! ctr (+ ctr 1))
  13384. (if (not (pair? clause))
  13385. (lint-format "case clause should be a list: ~A" caller (truncated-list->string clause))
  13386. (let ((keys (car clause))
  13387. (exprs (cdr clause)))
  13388. (if (null? exprs)
  13389. ;; (case x (0))
  13390. (lint-format "clause result is missing: ~A" caller clause))
  13391. (if (eq? result :unset)
  13392. (set! result exprs)
  13393. (if (not (equal? result exprs))
  13394. (set! result :unequal)))
  13395. (if (member exprs all-exprs)
  13396. (set! exprs-repeated exprs)
  13397. (set! all-exprs (cons exprs all-exprs)))
  13398. (if (and (pair? exprs)
  13399. (null? (cdr exprs))
  13400. (pair? (car exprs))
  13401. (pair? (cdar exprs))
  13402. (null? (cddar exprs))
  13403. (equal? selector (cadar exprs)))
  13404. (if (and (eq? (caar exprs) 'not)
  13405. (not (memq #f keys)))
  13406. ;; (case x ((0) (f x)) ((1) (not x)))
  13407. (lint-format "in ~A, perhaps replace ~A with #f" caller clause (car exprs))
  13408. ;; (case x ((0 1) (abs x)))
  13409. (lint-format "perhaps use => here: ~A" caller
  13410. (lists->string clause (list keys '=> (caar exprs))))))
  13411. (if (pair? keys)
  13412. (if (not (proper-list? keys))
  13413. ;; (case x ((0) 1) ((1) 2) ((3 . 0) 4))
  13414. (lint-format (if (null? keys)
  13415. "null case key list: ~A"
  13416. "stray dot in case case key list: ~A")
  13417. caller (truncated-list->string clause))
  13418. (for-each
  13419. (lambda (key)
  13420. (if (or (vector? key)
  13421. (string? key)
  13422. (pair? key))
  13423. ;; (case x ((#(0)) 2))
  13424. (lint-format "case key ~S in ~S is unlikely to work (case uses eqv? but it is a ~A)" caller
  13425. key clause
  13426. (cond ((vector? key) 'vector)
  13427. ((pair? key) 'pair)
  13428. (else 'string))))
  13429. (if (member key all-keys)
  13430. ;; (case x ((0) 1) ((1) 2) ((3 0) 4))
  13431. (lint-format "repeated case key ~S in ~S" caller key clause)
  13432. (set! all-keys (cons key all-keys)))
  13433. ;; unintentional quote here, as in (case x ('a b)...) never happens and
  13434. ;; is hard to distinguish from (case x ((quote a) b)...) which happens a lot
  13435. (if (not (compatible? sel-type (->lint-type key)))
  13436. ;; (case (string->symbol x) ((a) 1) ((2 3) 3))
  13437. (lint-format "case key ~S in ~S is pointless" caller key clause)))
  13438. keys))
  13439. (if (not (eq? keys 'else))
  13440. ;; (case ((1) 1) (t 2))
  13441. (lint-format "bad case key ~S in ~S" caller keys clause)
  13442. (begin
  13443. (set! has-else clause)
  13444. ;; exprs: (res) or if case, ((case ...)...)
  13445. (if (not (= ctr len))
  13446. ;; (case x (else 2) ((0) 1))
  13447. (lint-format "case else clause is not the last: ~A"
  13448. caller
  13449. (truncated-list->string (cddr form)))
  13450. (when (and (pair? exprs)
  13451. (pair? (car exprs))
  13452. (null? (cdr exprs)))
  13453. (let ((expr (car exprs)))
  13454. (case (car expr)
  13455. ((case) ; just the case statement in the else clause
  13456. (when (and (equal? selector (cadr expr))
  13457. (not (side-effect? selector env)))
  13458. (set! else-foldable (cddr expr))))
  13459. ((if) ; just if -- if foldable, make it look like it came from case
  13460. (when (and (equal? selector (eqv-selector (cadr expr)))
  13461. (cond-eqv? (cadr expr) selector #t)
  13462. (not (side-effect? selector env)))
  13463. ;; else-foldable as (((keys-from-test) true-branch) (else false-branch))
  13464. (set! else-foldable
  13465. (if (pair? (cdddr expr))
  13466. `(,(case-branch (cadr expr) selector (list (caddr expr)))
  13467. (else ,(car (cdddr expr))))
  13468. (list (case-branch (cadr expr) selector (cddr expr))))))))))))))
  13469. (lint-walk-open-body caller (car form) exprs env))))
  13470. (cddr form))
  13471. (if (and has-else
  13472. (pair? result)
  13473. (not else-foldable))
  13474. (begin
  13475. ;; (case x (else (case x (else 1)))) -> 1
  13476. (lint-format "perhaps ~A" caller (lists->string form
  13477. (if (null? (cdr result))
  13478. (car result)
  13479. `(begin ,@result))))
  13480. (set! exprs-repeated #f)))
  13481. ;; repeated result (but not all completely equal) and with else never happens
  13482. (when (or exprs-repeated else-foldable)
  13483. (let ((new-keys-and-exprs ())
  13484. (mergers ())
  13485. (else-clause (if else-foldable
  13486. (call-with-exit
  13487. (lambda (return)
  13488. (for-each (lambda (c) (if (eq? (car c) 'else) (return c))) else-foldable)
  13489. ()))
  13490. (or has-else ()))))
  13491. (let ((merge-case-keys
  13492. (let ((else-exprs (and (pair? else-clause) (cdr else-clause))))
  13493. (define (a-few lst)
  13494. (if (> (length lst) 3)
  13495. (copy lst (make-list 4 '...) 0 3)
  13496. lst))
  13497. (lambda (clause)
  13498. (let ((keys (car clause))
  13499. (exprs (cdr clause)))
  13500. (when (and (pair? exprs) ; ignore clauses that are messed up
  13501. (not (eq? keys 'else))
  13502. (not (equal? exprs else-exprs)))
  13503. (let ((prev (member exprs new-keys-and-exprs (lambda (a b) (equal? a (cdr b))))))
  13504. (if prev
  13505. (let* ((cur-clause (car prev))
  13506. (cur-keys (car cur-clause)))
  13507. (when (pair? cur-keys)
  13508. (set! mergers (cons (list (a-few keys) (a-few cur-keys)) mergers))
  13509. (set-car! cur-clause
  13510. (append cur-keys
  13511. (map (lambda (key)
  13512. (if (memv key cur-keys) (values) key))
  13513. keys)))))
  13514. (set! new-keys-and-exprs (cons (cons (copy (car clause))
  13515. (cdr clause))
  13516. new-keys-and-exprs))))))))))
  13517. (for-each merge-case-keys (cddr form))
  13518. (if (pair? else-foldable)
  13519. (for-each merge-case-keys else-foldable)))
  13520. (if (null? new-keys-and-exprs)
  13521. (lint-format "perhaps ~A" caller
  13522. ;; (case x (else (case x (else 1)))) -> 1
  13523. (lists->string form
  13524. (if (or (null? else-clause) ; can this happen? (it's caught above as an error)
  13525. (null? (cdr else-clause)))
  13526. ()
  13527. (if (null? (cddr else-clause))
  13528. (cadr else-clause)
  13529. `(begin ,@(cdr else-clause))))))
  13530. (begin
  13531. ;; (null? (cdr new-keys-and-exprs)) is rare and kinda dumb -- cases look like test suite entries
  13532. (for-each
  13533. (lambda (clause)
  13534. (if (and (pair? (car clause))
  13535. (pair? (cdar clause)))
  13536. (if (every? integer? (car clause))
  13537. (set-car! clause (sort! (car clause) <))
  13538. (if (every? char? (car clause))
  13539. (set-car! clause (sort! (car clause) char<?))))))
  13540. new-keys-and-exprs)
  13541. (let ((new-form (if (pair? else-clause)
  13542. `(case ,(cadr form) ,@(reverse new-keys-and-exprs) ,else-clause)
  13543. `(case ,(cadr form) ,@(reverse new-keys-and-exprs)))))
  13544. ;; (case x ((0) 32) ((1) 32)) -> (case x ((0 1) 32))
  13545. (lint-format "perhaps ~A" caller
  13546. (if (pair? mergers)
  13547. (format #f "merge keys ~{~{~A with ~A~}~^, ~}: ~A"
  13548. (reverse mergers)
  13549. (lists->string form new-form))
  13550. (lists->string form new-form)))))))))))
  13551. env)))
  13552. (hash-table-set! h 'case case-walker))
  13553. ;; ---------------- do ----------------
  13554. (let ((cxars (hash-table '(car . ()) '(caar . car) '(cdar . cdr)
  13555. '(caaar . caar) '(cdaar . cdar) '(cddar . cddr) '(cadar . cadr)
  13556. '(caaaar . caaar) '(caadar . caadr) '(cadaar . cadar) '(caddar . caddr)
  13557. '(cdaaar . cdaar) '(cdadar . cdadr) '(cddaar . cddar) '(cdddar . cdddr))))
  13558. (define (car-subst sym new-sym tree)
  13559. (cond ((or (not (pair? tree))
  13560. (eq? (car tree) 'quote))
  13561. tree)
  13562. ((not (and (symbol? (car tree))
  13563. (pair? (cdr tree))
  13564. (null? (cddr tree))
  13565. (eq? sym (cadr tree))))
  13566. (cons (car-subst sym new-sym (car tree))
  13567. (car-subst sym new-sym (cdr tree))))
  13568. ((hash-table-ref cxars (car tree)) => (lambda (f) (if (symbol? f) (list f new-sym) new-sym)))
  13569. (else tree)))
  13570. (define (cadr-subst sym new-sym tree)
  13571. ;(format *stderr* "subst: ~A ~A ~A~%" sym new-sym tree)
  13572. (cond ((or (not (pair? tree))
  13573. (eq? (car tree) 'quote))
  13574. tree)
  13575. ((and (memq (car tree) '(vector-ref string-ref list-ref))
  13576. (pair? (cdr tree))
  13577. (pair? (cddr tree))
  13578. (null? (cdddr tree))
  13579. (equal? sym (cadr tree)))
  13580. new-sym)
  13581. (else
  13582. (cons (cadr-subst sym new-sym (car tree))
  13583. (cadr-subst sym new-sym (cdr tree))))))
  13584. (define (var-step v) ((cdr v) 'step))
  13585. (define (do-walker caller form env)
  13586. (let ((vars ()))
  13587. (if (not (and (>= (length form) 3)
  13588. (proper-list? (cadr form))
  13589. (proper-list? (caddr form))))
  13590. (lint-format "do is messed up: ~A" caller (truncated-list->string form))
  13591. (let ((step-vars (cadr form))
  13592. (inner-env #f))
  13593. ;; do+lambda in body with stepper as free var never happens
  13594. (unless (side-effect? form env)
  13595. ;; a much more permissive check here (allowing sets of locals etc) got only a half-dozen hits
  13596. (let ((end+result (caddr form)))
  13597. (if (or (not (pair? end+result))
  13598. (null? (cdr end+result)))
  13599. ;; (do ((i 0 (+ i 1))) ((= i 1)))
  13600. (lint-format "this do-loop could be replaced by (): ~A" caller (truncated-list->string form))
  13601. (if (and (null? (cddr end+result))
  13602. (code-constant? (cadr end+result)))
  13603. ;; (begin (z 1) (do ((i 0 (+ i 1))) ((= i n) 32))): 32
  13604. (lint-format "this do-loop could be replaced by ~A: ~A" caller (cadr end+result) (truncated-list->string form))))))
  13605. ;; walk the init forms before adding the step vars to env
  13606. (do ((bindings step-vars (cdr bindings)))
  13607. ((not (pair? bindings))
  13608. (if (not (null? bindings))
  13609. (lint-format "do variable list is not a proper list? ~S" caller step-vars)))
  13610. (when (binding-ok? caller 'do (car bindings) env #f)
  13611. (for-each (lambda (v)
  13612. (if (not (or (eq? (var-initial-value v) (var-name v))
  13613. (not (tree-memq (var-name v) (cadar bindings)))
  13614. (hash-table-ref built-in-functions (var-name v))
  13615. (tree-table-member binders (cadar bindings))))
  13616. (if (not (var-member (var-name v) env))
  13617. ;; (let ((xx 0)) (do ((x 1 (+ x 1)) (y x (- y 1))) ((= x 3) xx) (display y)): x
  13618. (lint-format "~A in ~A does not appear to be defined in the calling environment" caller
  13619. (var-name v) (car bindings))
  13620. ;; (let ((x 0)) (do ((x 1 (+ x 1)) (y x (- y 1))) ((= x 3)) (display y))): y
  13621. (lint-format "~A in ~A refers to the caller's ~A, not the do-loop variable" caller
  13622. (var-name v) (car bindings) (var-name v)))))
  13623. vars)
  13624. (lint-walk caller (cadar bindings) env)
  13625. (let ((new-var (let ((v (make-var :name (caar bindings)
  13626. :definer 'do
  13627. :initial-value (cadar bindings))))
  13628. (let ((stepper (and (pair? (cddar bindings)) (caddar bindings))))
  13629. (varlet (cdr v) :step stepper)
  13630. (if stepper (set! (var-history v) (cons (list 'set! (caar bindings) stepper) (var-history v)))))
  13631. v)))
  13632. (set! vars (cons new-var vars)))))
  13633. (set! inner-env (append vars env))
  13634. ;; walk the step exprs
  13635. (let ((baddies ())) ; these are step vars (with step exprs) used within other step vars step expressions
  13636. (for-each (lambda (stepper)
  13637. (when (and (binding-ok? caller 'do stepper env #t)
  13638. (pair? (cddr stepper)))
  13639. (let ((data (var-member (car stepper) vars)))
  13640. (let ((old-ref (var-ref data)))
  13641. (lint-walk caller (caddr stepper) inner-env)
  13642. (set! (var-ref data) old-ref))
  13643. (if (eq? (car stepper) (caddr stepper))
  13644. (lint-format "perhaps ~A" caller (lists->string stepper (list (car stepper) (cadr stepper)))))
  13645. (set! (var-set data) (+ (var-set data) 1)))
  13646. (when (and (pair? (caddr stepper))
  13647. (not (eq? (car stepper) (cadr stepper)))
  13648. (eq? (car (caddr stepper)) 'cdr)
  13649. (eq? (cadr stepper) (cadr (caddr stepper))))
  13650. (lint-format "this looks suspicious: ~A" caller stepper))
  13651. (for-each (lambda (v)
  13652. (if (and (var-step v)
  13653. (not (eq? (var-name v) (car stepper)))
  13654. (or (eq? (var-name v) (caddr stepper))
  13655. (and (pair? (caddr stepper))
  13656. (tree-unquoted-member (var-name v) (caddr stepper)))))
  13657. (set! baddies (cons (car stepper) baddies))))
  13658. vars)))
  13659. step-vars)
  13660. (check-unordered-exprs caller form (map var-initial-value vars) env)
  13661. (when (pair? baddies)
  13662. ;; (do ((i 0 j) (j ...))...) is unreadable -- which (binding of) j is i set to?
  13663. ;; but this is tricky if there is more than one such variable -- if cross links, we'll need named let
  13664. ;; and if no step expr, there's no confusion.
  13665. ;; (do ((i 0 j) (j 1 i) (k 0 (+ k 1))) ((= k 4)) (format *stderr* "~A ~A~%" i j))
  13666. ;; (let __1__ ((i 0) (j 1) (k 0)) (if (= k 4) () (begin (format *stderr* "~A ~A~%" i j) (__1__ j i (+ k 1)))))
  13667. (let ((new-steppers (map (lambda (stepper)
  13668. (if (memq (car stepper) baddies)
  13669. `(,(car stepper) ,(cadr stepper))
  13670. stepper))
  13671. step-vars))
  13672. (new-sets (map (lambda (stepper)
  13673. (if (memq (car stepper) baddies)
  13674. `(set! ,(car stepper) ,(caddr stepper))
  13675. (values)))
  13676. step-vars)))
  13677. (if (or (null? (cdr baddies))
  13678. (let ((trails new-sets))
  13679. (not (any? (lambda (v) ; for each baddy, is it used in any following set!?
  13680. (and (pair? (cdr trails))
  13681. (set! trails (cdr trails))
  13682. (tree-unquoted-member v trails)))
  13683. (reverse baddies)))))
  13684. (lint-format "perhaps ~A" caller
  13685. (lists->string form
  13686. `(do ,new-steppers
  13687. ,(caddr form)
  13688. ,@(cdddr form)
  13689. ,@new-sets)))
  13690. ;; (do ((i 0 (+ i j)) (j 0 (+ k 1)) (k 1)) ((= i 10)) (display (+ i j k))) ->
  13691. ;; (do ((i 0) (j 0 (+ k 1)) (k 1)) ((= i 10)) (display (+ i j k)) (set! i (+ i j)))
  13692. (let* ((loop (find-unique-name form))
  13693. (new-body (let ((let-loop `(,loop ,@(map (lambda (s)
  13694. ((if (pair? (cddr s)) caddr car) s))
  13695. step-vars))))
  13696. (if (pair? (cdddr form))
  13697. `(begin ,@(cdddr form) ,let-loop)
  13698. let-loop))))
  13699. (let ((test (if (pair? (caddr form))
  13700. (caaddr form)
  13701. ()))
  13702. (result (if (not (and (pair? (caddr form))
  13703. (pair? (cdaddr form))))
  13704. ()
  13705. (if (null? (cdr (cdaddr form)))
  13706. (car (cdaddr form))
  13707. `(begin ,@(cdaddr form))))))
  13708. ;; (do ((i 0 j) (j 1 i) (k 0 (+ k 1))) ((= k 5) (set! x k) (+ k 1)) (display (+ i j)) -> use named let
  13709. (lint-format "this do loop is unreadable; perhaps ~A" caller
  13710. (lists->string form
  13711. `(let ,loop ,(map (lambda (s)
  13712. (list (car s) (cadr s)))
  13713. step-vars)
  13714. (if ,test ,result ,new-body))))))))))
  13715. ;; walk the body and end stuff (it's too tricky to find infinite do loops)
  13716. (when (pair? (caddr form))
  13717. (let ((end+result (caddr form)))
  13718. (when (pair? end+result)
  13719. (let ((end (car end+result)))
  13720. (lint-walk caller end inner-env) ; this will call simplify-boolean
  13721. (if (pair? (cdr end+result))
  13722. (if (null? (cddr end+result))
  13723. (begin
  13724. (if (any-null? (cadr end+result))
  13725. ;; (do ((i 0 (+ i 1))) ((= i 3) ()) (display i))
  13726. (lint-format "nil return value is redundant: ~A" caller end+result))
  13727. (lint-walk caller (cadr end+result) inner-env))
  13728. (lint-walk-open-body caller 'do-result (cdr end+result) inner-env)))
  13729. (if (and (symbol? end) (memq end '(= > < >= <= null? not)))
  13730. ;; (do ((i 0 (+ i 1))) (= i 10) (display i))
  13731. (lint-format "perhaps missing parens: ~A" caller end+result))
  13732. (cond ((never-false end)
  13733. ;; (do ((i 0 (+ i 1))) ((+ i 10) i))
  13734. (lint-format "end test is never false: ~A" caller end))
  13735. (end ; it's not #f
  13736. (if (never-true end)
  13737. (lint-format "end test is never true: ~A" caller end)
  13738. (let ((v (and (pair? end)
  13739. (memq (car end) '(< > <= >=))
  13740. (pair? (cdr end))
  13741. (symbol? (cadr end))
  13742. (var-member (cadr end) vars))))
  13743. ;; if found, v is the var info
  13744. (when (pair? v)
  13745. (let ((step (var-step v)))
  13746. (when (pair? step)
  13747. (let ((inc (and (memq (car step) '(+ -))
  13748. (pair? (cdr step))
  13749. (pair? (cddr step))
  13750. (or (and (real? (cadr step)) (cadr step))
  13751. (and (real? (caddr step)) (caddr step))))))
  13752. (when (and (real? inc)
  13753. (case (car step)
  13754. ((+) (and (positive? inc)
  13755. (memq (car end) '(< <=))))
  13756. ((-) (and (positive? inc)
  13757. (memq (car end) '(> >=))))
  13758. (else #f)))
  13759. ;; (do ((i 0 (+ i 1))) ((< i len)) (display i)
  13760. ;; (do ((i 0 (- i 1))) ((> i len)) (display i))
  13761. (lint-format "do step looks like it doesn't match end test: ~A" caller
  13762. (lists->string step end))))))))))
  13763. ((pair? (cdr end+result))
  13764. ;; (do ((i 0 (+ i 1))) (#f i))
  13765. (lint-format "result is unreachable: ~A" caller end+result)))
  13766. (if (and (symbol? end)
  13767. (not (var-member end env))
  13768. (procedure? (symbol->value end *e*)))
  13769. ;; (do ((i 0 (+ i 1))) (abs i) (display i))
  13770. (lint-format "strange do end-test: ~A in ~A is a procedure" caller end end+result))))))
  13771. (lint-walk-body caller 'do (cdddr form) (cons (make-var :name :let
  13772. :initial-value form
  13773. :definer 'do)
  13774. inner-env))
  13775. ;; before report-usage, check for unused variables, and don't complain about them if
  13776. ;; they are referenced in an earlier step expr.
  13777. (do ((v vars (cdr v)))
  13778. ((null? v))
  13779. (let ((var (car v)))
  13780. (when (zero? (var-ref var))
  13781. ;; var was not seen in the end+result/body or any subsequent step exprs
  13782. ;; vars is reversed order, so we need only scan var-step of the rest
  13783. (if (side-effect? (var-step var) env)
  13784. (set! (var-ref var) (+ (var-ref var) 1))
  13785. (for-each
  13786. (lambda (nv)
  13787. (if (or (eq? (var-name var) (var-step nv))
  13788. (and (pair? (var-step nv))
  13789. (tree-unquoted-member (var-name var) (var-step nv))))
  13790. (set! (var-ref var) (+ (var-ref var) 1))))
  13791. (cdr v))))))
  13792. (report-usage caller 'do vars inner-env)
  13793. ;; look for constant expressions in the do body
  13794. (when *report-constant-expressions-in-do*
  13795. (let ((constant-exprs (find-constant-exprs 'do (map var-name vars) (cdddr form))))
  13796. (when (pair? constant-exprs)
  13797. (if (null? (cdr constant-exprs))
  13798. ;; (do ((p (list 1) (cdr p))) ((null? p)) (set! y (log z 2)) (display x))
  13799. (lint-format "in ~A, ~A appears to be constant" caller
  13800. (truncated-list->string form)
  13801. (car constant-exprs))
  13802. (lint-format "in ~A, the following expressions appear to be constant:~%~NC~A" caller
  13803. (truncated-list->string form)
  13804. (+ lint-left-margin 4) #\space
  13805. (format #f "~{~A~^, ~}" constant-exprs))))))
  13806. ;; if simple lambda expanded and exists only for the loop, remove let as well?
  13807. ;; this can sometimes be simplified
  13808. (let ((body (cdddr form)))
  13809. (when (and (pair? body)
  13810. (null? (cdr body))
  13811. (pair? (car body)))
  13812. ;; do+let: tons of hits but how to distinguish the rewritable ones?
  13813. ;; very tricky if val is not a constant
  13814. (if (and (eq? (caar body) 'let)
  13815. (not (symbol? (cadar body)))
  13816. (every? (lambda (c) (code-constant? (cadr c))) (cadar body)))
  13817. ;; (do ((i 0 (+ i 1))) ((= i 3)) (let ((a 12)) (set! a (+ a i)) (display a))) ->
  13818. ;; (do ((i 0 (+ i 1)) (a 12 12)) ((= i 3)) (set! a (+ a i)) ...)
  13819. (lint-format "perhaps ~A" caller
  13820. (lists->string form
  13821. `(do (,@(cadr form)
  13822. ,@(map (lambda (c)
  13823. (list (car c) (cadr c) (cadr c)))
  13824. (cadar body)))
  13825. ,(caddr form)
  13826. ,@(one-call-and-dots (cddar body))))))
  13827. (let ((v (var-member (caar body) env)))
  13828. (when (and (var? v)
  13829. (memq (var-ftype v) '(define lambda)))
  13830. (let* ((vfunc (var-initial-value v))
  13831. (vbody (cddr vfunc)))
  13832. ;; we already detect a do body with no side-effects (walk-body)
  13833. (when (and (proper-list? ((if (eq? (var-ftype v) 'define) cdadr cadr) vfunc))
  13834. (null? (cdr vbody))
  13835. (< (tree-leaves vbody) 16))
  13836. (do ((pp (var-arglist v) (cdr pp)))
  13837. ((or (null? pp)
  13838. (> (tree-count1 (car pp) vbody 0) 1))
  13839. (when (null? pp)
  13840. (let ((new-body (copy vbody)))
  13841. (for-each (lambda (par arg)
  13842. (if (not (eq? par arg))
  13843. (set! new-body (tree-subst arg par new-body))))
  13844. (var-arglist v)
  13845. (cdar body))
  13846. ;; (do ((i 0 (+ i 1))) ((= i 10)) (f i)) -> (do ((i 0 (+ i 1))) ((= i 10)) (abs (* 2 i)))
  13847. (lint-format "perhaps ~A" caller
  13848. (lists->string form
  13849. `(do ,(cadr form)
  13850. ,(caddr form)
  13851. ,@new-body)))))))))))))
  13852. ;; do -> for-each
  13853. (when (and (pair? step-vars)
  13854. (null? (cdr step-vars)))
  13855. (let ((var (car step-vars)))
  13856. (when (and (pair? (cdr var))
  13857. (pair? (cddr var))
  13858. (pair? (caddr var))
  13859. (pair? (caddr form))
  13860. (pair? (caaddr form))
  13861. (null? (cdaddr form))
  13862. (pair? (cdaddr var))
  13863. (eq? (car var) (cadr (caddr var))))
  13864. (let ((vname (car var))
  13865. (end (caaddr form)))
  13866. (case (caaddr var)
  13867. ((cdr)
  13868. (when (and (case (car end)
  13869. ((null?)
  13870. (eq? (cadr end) vname))
  13871. ((not)
  13872. (and (pair? (cadr end))
  13873. (eq? (caadr end) 'pair?)
  13874. (eq? (cadadr end) vname)))
  13875. (else #f))
  13876. (not (let walker ((tree (cdddr form))) ; since only (cxar sym) is accepted, surely sym can't be shadowed?
  13877. (or (eq? tree vname)
  13878. (and (pair? tree)
  13879. (or (and (match-cxr 'cdr (car tree))
  13880. (pair? (cdr tree))
  13881. (eq? vname (cadr tree)))
  13882. (and (not (hash-table-ref cxars (car tree)))
  13883. (or (walker (car tree))
  13884. (walker (cdr tree))))))))))
  13885. ;; this assumes slightly more than the do-loop if (not (pair? var)) is the end-test
  13886. ;; for-each wants a sequence, but the do loop checks that in advance.
  13887. ;; (do ((p lst (cdr p))) ((null? p)) (display (car p))) -> (for-each (lambda ([p]) (display [p])) lst)
  13888. (lint-format "perhaps ~A" caller
  13889. (lists->string form
  13890. (let ((new-sym (symbol "[" (symbol->string vname) "]")))
  13891. `(for-each (lambda (,new-sym)
  13892. ,@(car-subst vname new-sym (cdddr form)))
  13893. ,(cadr var)))))))
  13894. ((+)
  13895. (when (and (eqv? (cadr var) 0)
  13896. (pair? (cddr (caddr var)))
  13897. (eqv? (caddr (caddr var)) 1)
  13898. (null? (cdddr (caddr var))))
  13899. (let ((end-var ((if (eq? vname (cadr end)) caddr cadr) end)))
  13900. (if (and (pair? end-var)
  13901. (memq (car end-var) '(length string-length vector-length)))
  13902. (set! end-var (cadr end-var))
  13903. (let ((v (var-member end-var env)))
  13904. (if (and (var? v)
  13905. (pair? (var-initial-value v))
  13906. (memq (car (var-initial-value v)) '(length string-length vector-length)))
  13907. (set! end-var (cadr (var-initial-value v))))))
  13908. (when (and (memq (car end) '(= >=))
  13909. (memq vname end)
  13910. (tree-memq vname (cdddr form))
  13911. (not (let walker ((tree (cdddr form)))
  13912. (if (and (pair? tree)
  13913. (memq vname tree)
  13914. (memq (car tree) '(string-ref list-ref vector-ref))
  13915. (eq? (caddr tree) vname))
  13916. (not (equal? (cadr tree) end-var))
  13917. (or (eq? tree vname)
  13918. (and (pair? tree)
  13919. (if (memq vname tree)
  13920. (not (and (memq (car tree) '(string-ref list-ref vector-ref))
  13921. (pair? (cdr tree))
  13922. (pair? (cddr tree))
  13923. (eq? (caddr tree) vname)))
  13924. (or (walker (car tree))
  13925. (walker (cdr tree))))))))))
  13926. ;; (do ((i 0 (+ i 1))) ((= i (vector-length x))) (find (vector-ref x i))) ->
  13927. ;; (for-each (lambda ([x]) (find [x])) x)
  13928. (lint-format "perhaps ~A" caller
  13929. (lists->string form
  13930. (let ((new-sym (symbol "[" (symbol->string (if (symbol? end-var) end-var (car end-var))) "]")))
  13931. `(for-each (lambda (,new-sym)
  13932. ,@(cadr-subst end-var new-sym (cdddr form)))
  13933. ,end-var)))))))))))))
  13934. ;; check for do-loop as copy/fill! stand-in and other similar cases
  13935. (when (and (pair? vars)
  13936. (null? (cdr vars)))
  13937. (let ((end-test (and (pair? (caddr form)) (caaddr form)))
  13938. (first-var (car step-vars))
  13939. (body (cdddr form))
  13940. (setv #f))
  13941. (when (and (pair? end-test)
  13942. (pair? body)
  13943. (null? (cdr body))
  13944. (pair? (car body))
  13945. (memq (car end-test) '(>= =)))
  13946. (let ((vname (car first-var))
  13947. (start (cadr first-var))
  13948. (step (and (pair? (cddr first-var))
  13949. (caddr first-var)))
  13950. (end (caddr end-test)))
  13951. (when (and (pair? step)
  13952. (eq? (car step) '+)
  13953. (memq vname step)
  13954. (memv 1 step)
  13955. (null? (cdddr step))
  13956. (or (eq? (cadr end-test) vname)
  13957. (and (eq? (car end-test) '=)
  13958. (eq? (caddr end-test) vname)
  13959. (set! end (cadr end-test)))))
  13960. ;; we have (do ((v start (+ v 1)|(+ 1 v))) ((= v end)|(= end v)|(>= v end)) one-statement)
  13961. (set! body (car body))
  13962. ;; write-char is the only other common case here -> write-string in a few cases
  13963. (when (and (memq (car body) '(vector-set! float-vector-set! int-vector-set! list-set! string-set! byte-vector-set!))
  13964. ;; integer type check here isn't needed because we're using this as an index below
  13965. ;; the type error will be seen in report-usage if not earlier
  13966. (eq? (caddr body) vname)
  13967. (let ((val (cadddr body)))
  13968. (set! setv val)
  13969. (or (code-constant? val)
  13970. (and (pair? val)
  13971. (memq (car val) '(vector-ref float-vector-ref int-vector-ref list-ref string-ref byte-vector-ref))
  13972. (eq? (caddr val) vname)))))
  13973. ;; (do ((i 2 (+ i 1))) ((= i len)) (string-set! s i #\a)) -> (fill! s #\a 2 len)
  13974. (lint-format "perhaps ~A" caller
  13975. (lists->string form
  13976. (if (code-constant? setv)
  13977. `(fill! ,(cadr body) ,(cadddr body) ,start ,end)
  13978. `(copy ,(cadr setv) ,(cadr body) ,start ,end))))))))))))
  13979. env))
  13980. (hash-table-set! h 'do do-walker))
  13981. ;; ---------------- let ----------------
  13982. (let ()
  13983. (define (let-walker caller form env)
  13984. (if (or (< (length form) 3)
  13985. (not (or (symbol? (cadr form))
  13986. (list? (cadr form)))))
  13987. ;; (let ((a 1) (set! a 2)))
  13988. (lint-format "let is messed up: ~A" caller (truncated-list->string form))
  13989. (let ((named-let (and (symbol? (cadr form)) (cadr form))))
  13990. (if (keyword? named-let)
  13991. ;; (let :x ((i y)) (x i))
  13992. (lint-format "bad let name: ~A" caller named-let))
  13993. (unless named-let
  13994. (if (and (null? (cadr form)) ; this can be fooled by macros that define things
  13995. (eq? form lint-current-form) ; i.e. we're in a body?
  13996. (not (tree-set-member '(call/cc call-with-current-continuation lambda lambda* define define*
  13997. define-macro define-macro* define-bacro define-bacro* define-constant define-expansion
  13998. load eval eval-string require)
  13999. (cddr form))))
  14000. ;; (begin (let () (display x)) y)
  14001. (lint-format "pointless let: ~A" caller (truncated-list->string form))
  14002. (let ((body (cddr form)))
  14003. (when (and (null? (cdr body))
  14004. (pair? (car body)))
  14005. (if (memq (caar body) '(let let*))
  14006. (if (null? (cadr form))
  14007. ;; (let () (let ((a x)) (+ a 1)))
  14008. (lint-format "pointless let: ~A" caller (lists->string form (car body)))
  14009. (if (null? (cadar body))
  14010. ;; (let ((a x)) (let () (+ a 1)))
  14011. (lint-format "pointless let: ~A" caller (lists->string form `(let ,(cadr form) ,@(cddar body))))))
  14012. (if (and (memq (caar body) '(lambda lambda*)) ; or any definer?
  14013. (null? (cadr form)))
  14014. ;; (let () (lambda (a b) (if (positive? a) (+ a b) b))) -> (lambda (a b) (if (positive? a) (+ a b) b))
  14015. (lint-format "pointless let: ~A" caller (lists->string form (car body)))))))))
  14016. (let ((vars (if (or (not named-let)
  14017. (keyword? named-let)
  14018. (not (or (null? (caddr form))
  14019. (and (proper-list? (caddr form))
  14020. (every? pair? (caddr form))))))
  14021. ()
  14022. (list (make-fvar :name named-let
  14023. :ftype 'let
  14024. :decl (dummy-func caller form (list 'define (cons '_ (map car (caddr form))) #f))
  14025. :arglist (map car (caddr form))
  14026. :initial-value form
  14027. :env env))))
  14028. (varlist ((if named-let caddr cadr) form))
  14029. (body ((if named-let cdddr cddr) form)))
  14030. (if (not (list? varlist))
  14031. (lint-format "let is messed up: ~A" caller (truncated-list->string form))
  14032. (if (and (null? varlist)
  14033. (pair? body)
  14034. (null? (cdr body))
  14035. (not (side-effect? (car body) env)))
  14036. ;; (let xx () z)
  14037. (lint-format "perhaps ~A" caller (lists->string form (car body)))))
  14038. (do ((bindings varlist (cdr bindings)))
  14039. ((not (pair? bindings))
  14040. (if (not (null? bindings))
  14041. ;; (let ((a 1) . b) a)
  14042. (lint-format "let variable list is not a proper list? ~S" caller varlist)))
  14043. (when (binding-ok? caller 'let (car bindings) env #f)
  14044. (let ((val (cadar bindings)))
  14045. (if (and (pair? val)
  14046. (eq? 'lambda (car val))
  14047. (tree-car-member (caar bindings) val)
  14048. (not (var-member (caar bindings) env)))
  14049. ;; (let ((x (lambda (a) (x 1)))) x)
  14050. (lint-format "let variable ~A is called in its binding? Perhaps let should be letrec: ~A"
  14051. caller (caar bindings)
  14052. (truncated-list->string bindings))
  14053. (unless named-let
  14054. (for-each (lambda (v)
  14055. (if (and (tree-memq (var-name v) (cadar bindings))
  14056. (not (hash-table-ref built-in-functions (var-name v)))
  14057. (not (tree-table-member binders (cadar bindings))))
  14058. (if (not (var-member (var-name v) env))
  14059. ;; (let ((x 1) (y x)) (+ x y)): x in (y x)
  14060. (lint-format "~A in ~A does not appear to be defined in the calling environment" caller
  14061. (var-name v) (car bindings))
  14062. ;; (let ((x 3)) (+ x (let ((x 1) (y x)) (+ x y)))): x in (y x)
  14063. (lint-format "~A in ~A refers to the caller's ~A, not the let variable" caller
  14064. (var-name v) (car bindings) (var-name v)))))
  14065. vars)))
  14066. (let ((e (if (symbol? val)
  14067. (set-ref val caller form env)
  14068. (lint-walk caller val env))))
  14069. (if (and (pair? e)
  14070. (not (eq? e env))
  14071. (memq (var-name (car e)) '(:lambda :dilambda)))
  14072. (let ((ldata (cdar e)))
  14073. (set! (var-name (car e)) (caar bindings))
  14074. (set! (ldata 'initial-value) val)
  14075. (set! vars (cons (car e) vars)))
  14076. (set! vars (cons (make-var :name (caar bindings)
  14077. :initial-value val
  14078. :definer (if named-let 'named-let 'let))
  14079. vars)))))))
  14080. (check-unordered-exprs caller form
  14081. (map (if (not named-let)
  14082. var-initial-value
  14083. (lambda (v)
  14084. (if (eq? (var-name v) named-let)
  14085. (values)
  14086. (var-initial-value v))))
  14087. vars)
  14088. env)
  14089. (let ((suggest made-suggestion))
  14090. (when (and (pair? varlist) ; (let ((x (A))) (if x (f x) B)) -> (cond ((A) => f) (else B)
  14091. (pair? body)
  14092. (pair? (car body))
  14093. (null? (cdr body))
  14094. (pair? (cdar body)))
  14095. (when (and (pair? (car varlist)) ; ^ this happens a lot, so it's worth this tedious search
  14096. (null? (cdr varlist)) ; also (let ((x (A))) (cond (x (f x))...)
  14097. (pair? (cdar varlist))
  14098. (pair? (cadar varlist)))
  14099. (let ((p (car body))
  14100. (vname (caar varlist))
  14101. (vvalue (cadar varlist)))
  14102. (when (and (not named-let) ; (let ((x (assq a y))) (set! z (if x (cadr x) 0))) -> (set! z (cond ((assq a y) => cadr) (else 0)))
  14103. (not (memq (car p) '(if cond))) ; handled separately below
  14104. (= (tree-count2 vname p 0) 2))
  14105. (do ((i 0 (+ i 1))
  14106. (bp (cdr p) (cdr bp)))
  14107. ((or (null? bp)
  14108. (let ((b (car bp)))
  14109. (and (pair? b)
  14110. (eq? (car b) 'if)
  14111. (= (tree-count2 vname b 0) 2)
  14112. (eq? vname (cadr b))
  14113. (pair? (caddr b))
  14114. (pair? (cdaddr b))
  14115. (null? (cddr (caddr b)))
  14116. (eq? vname (cadr (caddr b))))))
  14117. (if (pair? bp)
  14118. (let ((else-clause (if (pair? (cdddar bp)) `((else ,@(cdddar bp))) ())))
  14119. (lint-format "perhaps ~A" caller
  14120. (lists->string form `(,@(copy p (make-list (+ i 1)))
  14121. (cond (,vvalue => ,(caaddr (car bp))) ,@else-clause)
  14122. ,@(cdr bp)))))))))
  14123. (when (and (eq? (car p) 'cond) ; (let ((x (f y))) (cond (x (g x)) ...)) -> (cond ((f y) => g) ...)
  14124. (pair? (cadr p))
  14125. (eq? (caadr p) vname)
  14126. (pair? (cdadr p))
  14127. (null? (cddadr p))
  14128. (or (and (pair? (cadadr p))
  14129. (pair? (cdr (cadadr p)))
  14130. (null? (cddr (cadadr p))) ; one arg to func
  14131. (eq? vname (cadr (cadadr p))))
  14132. (eq? vname (cadadr p)))
  14133. (or (null? (cddr p))
  14134. (not (tree-unquoted-member vname (cddr p)))))
  14135. (lint-format "perhaps ~A" caller
  14136. (lists->string form
  14137. (if (eq? vname (cadadr p))
  14138. (if (and (pair? (cddr p))
  14139. (pair? (caddr p))
  14140. (memq (caaddr p) '(else #t t)))
  14141. (if (null? (cddr (caddr p)))
  14142. `(or ,vvalue ,(cadr (caddr p)))
  14143. `(or ,vvalue (begin ,@(cdaddr p))))
  14144. `(or ,vvalue
  14145. (cond ,@(cddr p))))
  14146. `(cond (,vvalue => ,(caadr (cadr p)))
  14147. ,@(cddr p))))))
  14148. (when (and (null? (cddr p)) ; (let ((x (+ y 1))) (abs x)) -> (abs (+ y 1))
  14149. (eq? vname (cadr p))) ; not tree-subst or trailing (pair) args: the let might be forcing evaluation order
  14150. (let ((v (var-member (car p) env)))
  14151. (if (or (and (var? v)
  14152. (memq (var-definer v) '(define define* lambda lambda*)))
  14153. (hash-table-ref built-in-functions (car p)))
  14154. (lint-format "perhaps ~A" caller (lists->string form `(,(car p) ,vvalue)))
  14155. (if (not (or (any-macro? vname env)
  14156. (tree-unquoted-member vname (car p))))
  14157. (lint-format "perhaps, assuming ~A is not a macro, ~A" caller (car p)
  14158. (lists->string form `(,(car p) ,vvalue)))))))
  14159. (when (pair? (cddr p))
  14160. (when (and (eq? (car p) 'if)
  14161. (pair? (cdddr p)))
  14162. (let ((if-true (caddr p))
  14163. (if-false (cadddr p)))
  14164. (when (and (eq? (cadr p) vname) ; (let ((x (g y))) (if x #t #f)) -> (g y)
  14165. (boolean? if-true)
  14166. (boolean? if-false)
  14167. (not (eq? if-true if-false)))
  14168. (lint-format "perhaps ~A" caller
  14169. (lists->string form (if if-true vvalue `(not ,vvalue)))))
  14170. (when (and (pair? (cadr p)) ; (let ((x (f y))) (if (not x) B (g x))) -> (cond ((f y) => g) (else B))
  14171. (eq? (caadr p) 'not)
  14172. (eq? (cadadr p) vname)
  14173. (pair? if-false)
  14174. (pair? (cdr if-false))
  14175. (null? (cddr if-false))
  14176. (eq? vname (cadr if-false)))
  14177. (let ((else-clause (if (eq? if-true vname)
  14178. `((else #f))
  14179. (if (and (pair? if-true)
  14180. (tree-unquoted-member vname if-true))
  14181. :oops! ; if the let var appears in the else portion, we can't do anything with =>
  14182. `((else ,if-true))))))
  14183. (unless (eq? else-clause :oops!)
  14184. (lint-format "perhaps ~A" caller (lists->string form `(cond (,vvalue => ,(car if-false)) ,@else-clause))))))))
  14185. (let ((crf #f))
  14186. ;; all this stuff still misses (cond ((not x)...)) and (set! y (if x (cdr x)...)) i.e. need embedding in this case
  14187. (when (and (or (and (memq (car p) '(if and)) ; (let ((x (f y))) (and x (g x))) -> (cond ((f y) => g) (else #f))
  14188. (eq? (cadr p) vname))
  14189. (and (eq? (car p) 'or)
  14190. (equal? (cadr p) `(not ,vname)))
  14191. (and (pair? vvalue)
  14192. (memq (car vvalue) '(assoc assv assq member memv memq))
  14193. (pair? (cadr p))
  14194. (or (eq? (caadr p) 'pair?)
  14195. (and (eq? (caadr p) 'null?)
  14196. ;; (let ((x (assoc y z))) (if (null? x) (g x)))
  14197. (lint-format "in ~A, ~A can't be null because ~A in ~A only returns #f or a pair"
  14198. caller p vname (car vvalue) (truncated-list->string (car varlist)))
  14199. #f))
  14200. (eq? (cadadr p) vname)))
  14201. (or (and (pair? (caddr p))
  14202. (pair? (cdaddr p))
  14203. (null? (cddr (caddr p))) ; one func arg
  14204. (or (eq? vname (cadr (caddr p)))
  14205. (and (hash-table-ref combinable-cxrs (caaddr p))
  14206. ((lambda* (cr arg) ; lambda* not lambda because combine-cxrs might return just #f
  14207. (and cr
  14208. (< (length cr) 5)
  14209. (eq? vname arg)
  14210. (set! crf (symbol "c" cr "r"))))
  14211. (combine-cxrs (caddr p))))))
  14212. (and (eq? (car p) 'if)
  14213. (eq? (caddr p) vname)
  14214. (not (tree-unquoted-member vname (cdddr p)))
  14215. ;; (let ((x (g y))) (if x x (g z))) -> (or (g y) (g z))
  14216. (lint-format "perhaps ~A" caller
  14217. (lists->string form
  14218. (if (null? (cdddr p))
  14219. vvalue
  14220. `(or ,vvalue ,(cadddr p)))))
  14221. #f))
  14222. (pair? (caddr p))
  14223. (or (eq? (car p) 'if)
  14224. (null? (cdddr p))))
  14225. (let ((else-clause (if (pair? (cdddr p))
  14226. (if (eq? (cadddr p) vname)
  14227. `((else #f)) ; this stands in for the local var
  14228. (if (and (pair? (cadddr p))
  14229. (tree-unquoted-member vname (cadddr p)))
  14230. :oops! ; if the let var appears in the else portion, we can't do anything with =>
  14231. `((else ,(cadddr p)))))
  14232. (case (car p)
  14233. ((and) '((else #f)))
  14234. ((or) '((else #t)))
  14235. (else ())))))
  14236. (unless (eq? else-clause :oops!)
  14237. ;; (let ((x (assoc y z))) (if x (cdr x))) -> (cond ((assoc y z) => cdr))
  14238. (lint-format "perhaps ~A" caller
  14239. (lists->string form `(cond (,vvalue => ,(or crf (caaddr p))) ,@else-clause))))))))
  14240. )) ; one var in varlist
  14241. ;; ----------------------------------------
  14242. ;; move let in:
  14243. ;; (let ((a (car x))) (if b (+ a (f a)) (display c))) -> (if b (let ((a (car x))) (+ a (f a))) (display c))
  14244. ;; let* version gets only 3 hits
  14245. (unless (or named-let
  14246. (any? (lambda (c)
  14247. (not (and (pair? c)
  14248. (symbol? (car c))
  14249. (pair? (cdr c))
  14250. (not (side-effect? (cadr c) env)))))
  14251. (cadr form)))
  14252. (case (caar body)
  14253. ((if)
  14254. (let ((test (cadar body))
  14255. (true (caddar body))
  14256. (false (and (pair? (cdddar body)) (car (cdddar body))))
  14257. (vars (map car (cadr form)))
  14258. (false-let #f))
  14259. (when (and (not (memq test vars))
  14260. (not (tree-set-member vars test))
  14261. (or (and (not (memq true vars))
  14262. (not (tree-set-member vars true))
  14263. (set! false-let #t))
  14264. (not false)
  14265. (not (or (memq false vars)
  14266. (tree-set-member vars false))))
  14267. (tree-set-member vars body)) ; otherwise we'll complain elsewhere about unused variables
  14268. (lint-format "perhaps move the let to the ~A branch: ~A" caller
  14269. (if false-let "false" "true")
  14270. (lists->string form
  14271. (let ((true-dots (if (> (tree-leaves true) 30) '... true))
  14272. (false-dots (if (and (pair? false) (> (tree-leaves false) 30)) '... false)))
  14273. (if false-let
  14274. `(if ,test ,true-dots (let ,(cadr form) ,@(unbegin false-dots)))
  14275. (if (pair? (cdddr (caddr form)))
  14276. `(if ,test (let ,(cadr form) ,@(unbegin true-dots)) ,false-dots)
  14277. `(if ,test (let ,(cadr form) ,@(unbegin true-dots)))))))))))
  14278. ((cond)
  14279. ;; happens about a dozen times
  14280. (let ((vars (map car (cadr form))))
  14281. (if (tree-set-member vars (cdar body))
  14282. (call-with-exit
  14283. (lambda (quit)
  14284. (let ((branch-let #f))
  14285. (for-each (lambda (c)
  14286. (if (and (not branch-let)
  14287. (side-effect? (car c) env))
  14288. (quit))
  14289. (when (and (pair? c)
  14290. (tree-set-member vars c))
  14291. (if branch-let (quit))
  14292. (set! branch-let c)))
  14293. (cdar body))
  14294. (if (and branch-let
  14295. (not (memq (car branch-let) vars))
  14296. (not (tree-set-member vars (car branch-let))))
  14297. (lint-format "perhaps move the let into the '~A branch: ~A" caller
  14298. (truncated-list->string branch-let)
  14299. (lists->string form
  14300. (if (eq? branch-let (cadar body))
  14301. `(cond (,(car branch-let) (let ,(cadr form) ,@(cdr branch-let))) ...)
  14302. `(cond ... (,(car branch-let) (let ,(cadr form) ,@(cdr branch-let))) ...)))))))))))
  14303. ((case)
  14304. (let ((vars (map car (cadr form)))
  14305. (test (cadar body)))
  14306. (if (and (not (memq test vars))
  14307. (not (tree-set-member vars test))
  14308. (tree-set-member vars (cddar body)))
  14309. (call-with-exit
  14310. (lambda (quit)
  14311. (let ((branch-let #f))
  14312. (for-each (lambda (c)
  14313. (when (and (pair? c)
  14314. (tree-set-member vars (cdr c)))
  14315. (if branch-let (quit))
  14316. (set! branch-let c)))
  14317. (cddar body))
  14318. (if branch-let
  14319. (lint-format "perhaps move the let into the '~A branch: ~A" caller
  14320. (truncated-list->string branch-let)
  14321. (lists->string form
  14322. (if (eq? branch-let (caddar body))
  14323. `(case ,test (,(car branch-let) (let ,(cadr form) ,@(cdr branch-let))) ...)
  14324. `(case ,test ... (,(car branch-let) (let ,(cadr form) ,@(cdr branch-let))) ...)))))))))))
  14325. ((when unless) ; no hits -- maybe someday?
  14326. (let ((test (cadar body))
  14327. (vars (map car (cadr form))))
  14328. (unless (or (memq test vars)
  14329. (tree-set-member vars test)
  14330. (side-effect? test env))
  14331. (lint-format "perhaps move the let inside the ~A: ~A" caller
  14332. (caar body)
  14333. (truncated-lists->string form `(,(caar body) ,test (let ,(cadr form) ,@(cddar body))))))))))
  14334. ;; ----------------------------------------
  14335. ;; (let ((x 1) (y 2)) (+ x y)) -> (+ 1 2)
  14336. ;; this happens a lot, but it often looks like a form of documentation
  14337. (when (and (= suggest made-suggestion)
  14338. (not named-let)
  14339. (< (length varlist) 8)
  14340. (not (memq (caar body) '(lambda lambda* define define* define-macro)))
  14341. (not (and (eq? (caar body) 'set!)
  14342. (any? (lambda (v) (eq? (car v) (cadar body))) varlist)))
  14343. (not (any-macro? (caar body) env))
  14344. (not (any? (lambda (p)
  14345. (and (pair? p)
  14346. (not (eq? (car p) 'quote))
  14347. (or (not (hash-table-ref no-side-effect-functions (car p)))
  14348. (any? pair? (cdr p)))))
  14349. (cdar body)))
  14350. (every? (lambda (v)
  14351. (and (pair? v)
  14352. (pair? (cdr v))
  14353. (< (tree-leaves (cadr v)) 8)
  14354. (= (tree-count1 (car v) body 0) 1)))
  14355. varlist))
  14356. (let ((new-body (copy (car body)))
  14357. (bool-arg? #f))
  14358. (for-each (lambda (v)
  14359. (if (not bool-arg?)
  14360. (let tree-walk ((tree body))
  14361. (if (pair? tree)
  14362. (if (and (memq (car tree) '(or and))
  14363. (memq (car v) (cdr tree)))
  14364. (set! bool-arg? #t)
  14365. (begin
  14366. (tree-walk (car tree))
  14367. (tree-walk (cdr tree)))))))
  14368. (set! new-body (tree-subst (cadr v) (car v) new-body)))
  14369. varlist)
  14370. (lint-format (if bool-arg?
  14371. "perhaps, ignoring short-circuit issues, ~A"
  14372. "perhaps ~A")
  14373. caller (lists->string form new-body))))
  14374. ) ; null cdr body etc
  14375. (when (and (pair? (cadr form)) ; (let ((x x)) (+ x 1)) -> (+ x 1), (let ((x x))...) does not copy x if x is a sequence
  14376. (= suggest made-suggestion)
  14377. (every? (lambda (c)
  14378. (and (pair? c) ; the usual... (let binding might be messed up)
  14379. (pair? (cdr c))
  14380. (eq? (car c) (cadr c))))
  14381. (cadr form))
  14382. (not (and (pair? (caddr form))
  14383. (memq (caaddr form) '(lambda lambda*)))))
  14384. (let ((vs (map car (cadr form))))
  14385. (unless (any? (lambda (p)
  14386. (and (pair? p)
  14387. (memq (cadr p) vs)
  14388. (or (eq? (car p) 'set!)
  14389. (set!? p env))))
  14390. (cddr form))
  14391. (lint-format "perhaps omit this useless let: ~A" caller
  14392. (truncated-lists->string form
  14393. (if (null? (cdddr form))
  14394. (caddr form)
  14395. `(begin ,@(cddr form))))))))
  14396. ) ; suggest let
  14397. (let* ((cur-env (cons (make-var :name :let
  14398. :initial-value form
  14399. :definer 'let)
  14400. (append vars env)))
  14401. (e (lint-walk-body (or named-let caller) 'let body cur-env)))
  14402. (let ((nvars (and (not (eq? e cur-env))
  14403. (env-difference caller e cur-env ()))))
  14404. (if (pair? nvars)
  14405. (if (memq (var-name (car nvars)) '(:lambda :dilambda))
  14406. (begin
  14407. (set! env (cons (car nvars) env))
  14408. (set! nvars (cdr nvars)))
  14409. (set! vars (append nvars vars)))))
  14410. (if (and (pair? body)
  14411. (equal? (list-ref body (- (length body) 1)) '(curlet))) ; the standard library tag
  14412. (for-each (lambda (v)
  14413. (set! (var-ref v) (+ (var-ref v) 1)))
  14414. e))
  14415. (report-usage caller 'let vars e)
  14416. ;; look for splittable lets and let-temporarily possibilities
  14417. (when (and (pair? vars)
  14418. (pair? (cadr form))
  14419. (pair? (caadr form)))
  14420. (for-each
  14421. (lambda (local-var)
  14422. (let ((vname (var-name local-var)))
  14423. ;; ideally we'd collect vars that fit into one let etc
  14424. (when (> (length body) (* 5 (var-set local-var)) 0)
  14425. (do ((i 0 (+ i 1))
  14426. (preref #f)
  14427. (p body (cdr p)))
  14428. ((or (not (pair? (cdr p)))
  14429. (and (pair? (car p))
  14430. (eq? (caar p) 'set!)
  14431. (eq? (cadar p) vname)
  14432. (> i 5)
  14433. (begin
  14434. (if (or preref
  14435. (side-effect? (var-initial-value local-var) env))
  14436. ;; (let ((x 32)) (display x) (set! y (f x)) (g (+ x 1) y) (a y) (f y) (g y) (h y) (i y) (set! x 3) (display x) (h y x))
  14437. ;; (let ... (let ((x 3)) ...))
  14438. (lint-format "perhaps add a new binding for ~A to replace ~A: ~A" caller
  14439. vname
  14440. (truncated-list->string (car p))
  14441. (lists->string form
  14442. `(let ...
  14443. (let ((,vname ,(caddar p)))
  14444. ...))))
  14445. ;; (let ((x 32)) (set! y (f 1)) (a y) (f y) (g y) (h y) (i y) (set! x (+ x... -> (let () ... (let ((x (+ 32 1))) ...))
  14446. (lint-format "perhaps move the ~A binding to replace ~A: ~A" caller
  14447. vname
  14448. (truncated-list->string (car p))
  14449. (let ((new-value (if (tree-member vname (caddar p))
  14450. (tree-subst (var-initial-value local-var) vname (copy (caddar p)))
  14451. (caddar p))))
  14452. (lists->string form
  14453. `(let ,(let rewrite ((lst (cadr form)))
  14454. (cond ((null? lst) ())
  14455. ((and (pair? (car lst))
  14456. (eq? (caar lst) vname))
  14457. (rewrite (cdr lst)))
  14458. (else (cons (if (< (tree-leaves (cadar lst)) 30)
  14459. (car lst)
  14460. (list (caar lst) '...))
  14461. (rewrite (cdr lst))))))
  14462. ...
  14463. (let ((,vname ,new-value))
  14464. ...))))))
  14465. #t))))
  14466. (if (tree-member vname (car p))
  14467. (set! preref i))))
  14468. (when (and (zero? (var-set local-var))
  14469. (= (var-ref local-var) 2)) ; initial value and set!
  14470. (do ((saved-name (var-initial-value local-var))
  14471. (p body (cdr p))
  14472. (last-pos #f)
  14473. (first-pos #f))
  14474. ((not (pair? p))
  14475. (when (and (pair? last-pos)
  14476. (not (eq? first-pos last-pos))
  14477. (not (tree-equal-member saved-name (cdr last-pos))))
  14478. ;; (let ((old-x x)) (set! x 12) (display (log x)) (set! x 1) (set! x old-x)) ->
  14479. ;; (let-temporarily ((x 12)) (display (log x)) (set! x 1))
  14480. (lint-format "perhaps use let-temporarily here: ~A" caller
  14481. (lists->string form
  14482. (let ((new-let `(let-temporarily
  14483. ((,saved-name ,(if (pair? first-pos)
  14484. (caddar first-pos)
  14485. saved-name)))
  14486. ,@(map (lambda (expr)
  14487. (if (or (and (pair? first-pos)
  14488. (eq? expr (car first-pos)))
  14489. (eq? expr (car last-pos)))
  14490. (values)
  14491. expr))
  14492. body))))
  14493. (if (null? (cdr vars)) ; we know vars is a pair, want len=1
  14494. new-let
  14495. `(let ,(map (lambda (v)
  14496. (if (eq? (car v) vname)
  14497. (values)
  14498. v))
  14499. (cadr form))
  14500. ,new-let)))))))
  14501. ;; someday maybe look for additional saved vars, but this happens only in snd-test
  14502. ;; also the let-temp could be reduced to the set locations (so the tree-equal-member
  14503. ;; check above would be unneeded).
  14504. (let ((expr (car p)))
  14505. (when (and (pair? expr)
  14506. (eq? (car expr) 'set!)
  14507. (equal? (cadr expr) saved-name)
  14508. (pair? (cddr expr)))
  14509. (if (not first-pos)
  14510. (set! first-pos p))
  14511. (if (eq? (caddr expr) vname)
  14512. (set! last-pos p))))))))
  14513. vars)))
  14514. (when (and (pair? varlist)
  14515. (pair? (car varlist))
  14516. (null? (cdr varlist)))
  14517. (if (and (pair? body) ; (let ((x y)) x) -> y, named let is possible here
  14518. (null? (cdr body))
  14519. (eq? (car body) (caar varlist))
  14520. (pair? (cdar varlist))) ; (let ((a))...)
  14521. (lint-format "perhaps ~A" caller (lists->string form (cadar varlist))))
  14522. ;; also (let ((x ...)) (let ((y x)...))) happens but it looks like automatically generated code or test suite junk
  14523. ;; copied from letrec below -- happens about a dozen times
  14524. (when (and (not named-let)
  14525. (pair? (cddr form))
  14526. (pair? (caddr form))
  14527. (null? (cdddr form)))
  14528. (let ((body (caddr form))
  14529. (sym (caar varlist))
  14530. (lform (and (pair? (caadr form))
  14531. (pair? (cdaadr form))
  14532. (cadar (cadr form)))))
  14533. (if (and (pair? lform)
  14534. (pair? (cdr lform))
  14535. (eq? (car lform) 'lambda)
  14536. (proper-list? (cadr lform)))
  14537. ;; unlike in letrec, here there can't be recursion (ref to same name is ref to outer env)
  14538. (if (eq? sym (car body))
  14539. (if (not (tree-memq sym (cdr body)))
  14540. ;; (let ((x (lambda (y) (+ y (x (- y 1)))))) (x 2)) -> (let ((y 2)) (+ y (x (- y 1))))
  14541. (lint-format "perhaps ~A" caller
  14542. (lists->string
  14543. form `(let ,(map list (cadr lform) (cdr body))
  14544. ,@(cddr lform)))))
  14545. (if (= (tree-count1 sym body 0) 1)
  14546. (let ((call (find-call sym body)))
  14547. (when (pair? call)
  14548. (let ((new-call `(let ,(map list (cadr lform) (cdr call))
  14549. ,@(cddr lform))))
  14550. ;; (let ((f60 (lambda (x) (* 2 x)))) (+ 1 (f60 y))) -> (+ 1 (let ((x y)) (* 2 x)))
  14551. (lint-format "perhaps ~A" caller
  14552. (lists->string form (tree-subst new-call call body))))))))))))
  14553. (when (pair? body)
  14554. (when (and (pair? (car body))
  14555. (pair? (cdar body))
  14556. (pair? (cddar body))
  14557. (eq? (caar body) 'set!))
  14558. (let ((settee (cadar body))
  14559. (setval (caddar body)))
  14560. (if (and (not named-let) ; (let ((x 0)...) (set! x 1)...) -> (let ((x 1)...)...)
  14561. (not (tree-memq 'curlet setval))
  14562. (cond ((assq settee vars)
  14563. => (lambda (v)
  14564. (or (and (code-constant? (var-initial-value v))
  14565. (code-constant? setval))
  14566. (not (any? (lambda (v1)
  14567. (or (tree-memq (car v1) setval)
  14568. (side-effect? (cadr v1) env)))
  14569. varlist)))))
  14570. (else #f)))
  14571. (lint-format "perhaps ~A" caller ; (let ((a 1)) (set! a 2)) -> 2
  14572. (lists->string form
  14573. (if (null? (cdr body)) ; this only happens in test suites...
  14574. (if (null? (cdr varlist))
  14575. setval
  14576. `(let ,(map (lambda (v) (if (eq? (car v) settee) (values) v)) varlist)
  14577. ,setval))
  14578. `(let ,(map (lambda (v)
  14579. (if (eq? (car v) settee)
  14580. (list (car v) setval)
  14581. v))
  14582. varlist)
  14583. ,@(if (null? (cddr body))
  14584. (cdr body)
  14585. `(,(cadr body) ...))))))
  14586. ;; repetition for the moment
  14587. (when (and (pair? varlist)
  14588. (assq settee vars) ; settee is a local var
  14589. (not (eq? settee named-let)) ; (let loop () (set! loop 3))!
  14590. (or (null? (cdr body))
  14591. (and (null? (cddr body))
  14592. (eq? settee (cadr body))))) ; (let... (set! local val) local)
  14593. (lint-format "perhaps ~A" caller
  14594. (lists->string form
  14595. (if (or (tree-memq settee setval)
  14596. (side-effect? (cadr (assq settee varlist)) env))
  14597. `(let ,varlist ,setval)
  14598. (if (null? (cdr varlist))
  14599. setval
  14600. `(let ,(remove-if (lambda (v)
  14601. (eq? (car v) settee))
  14602. varlist)
  14603. ,setval)))))))))
  14604. (unless named-let
  14605. ;; if var val is symbol, val not used (not set!) in body (even hidden via function call)
  14606. ;; and var not set!, and not a function parameter (we already reported those),
  14607. ;; remove it (the var) and replace with val throughout
  14608. (when (and (proper-list? (cadr form))
  14609. (not (tree-set-member '(curlet lambda lambda* define define*) (cddr form))))
  14610. (do ((changes ())
  14611. (vs (cadr form) (cdr vs)))
  14612. ((null? vs)
  14613. (if (pair? changes)
  14614. (let ((new-form (copy form)))
  14615. (for-each
  14616. (lambda (v)
  14617. (list-set! new-form 1 (remove-if (lambda (p) (equal? p v)) (cadr new-form)))
  14618. (set! new-form (tree-subst (cadr v) (car v) new-form)))
  14619. changes)
  14620. (lint-format "assuming we see all set!s, the binding~A ~{~A~^, ~} ~A pointless: perhaps ~A" caller
  14621. (if (pair? (cdr changes)) "s" "")
  14622. changes
  14623. (if (pair? (cdr changes)) "are" "is")
  14624. (lists->string form
  14625. (if (< (tree-leaves new-form) 200)
  14626. new-form
  14627. `(let ,(cadr new-form)
  14628. ,@(one-call-and-dots (cddr new-form)))))))))
  14629. (let ((v (car vs)))
  14630. (when (and (pair? v)
  14631. (pair? (cdr v))
  14632. (null? (cddr v)) ; good grief
  14633. (symbol? (cadr v))
  14634. (not (set-target (cadr v) body env))
  14635. (not (set-target (car v) body env))
  14636. (let ((data (var-member (cadr v) env)))
  14637. (or (not (var? data))
  14638. (and (not (eq? (var-definer data) 'parameter))
  14639. (or (null? (var-setters data))
  14640. (not (tree-set-member (var-setters data) body)))))))
  14641. (set! changes (cons v changes))))))
  14642. (when (pair? varlist)
  14643. ;; if last is (set! local-var...) and no complications, complain
  14644. (let ((last (list-ref body (- (length body) 1))))
  14645. (when (and (pair? last)
  14646. (eq? (car last) 'set!)
  14647. (pair? (cdr last))
  14648. (pair? (cddr last)) ; (set! a)
  14649. (symbol? (cadr last))
  14650. (assq (cadr last) varlist) ; (let ((a 1) (b (display 2))) (set! a 2))
  14651. ;; this is overly restrictive:
  14652. (not (tree-set-member '(call/cc call-with-current-continuation curlet lambda lambda*) form)))
  14653. (lint-format "set! is pointless in ~A: use ~A" caller
  14654. last (caddr last))))
  14655. (when (and (pair? (car body))
  14656. (eq? (caar body) 'do))
  14657. (when (and (null? (cdr body)) ; removing this restriction gets only 3 hits
  14658. (pair? (cadar body)))
  14659. (let ((inits (map cadr (cadar body))))
  14660. (when (every? (lambda (v)
  14661. (and (= (tree-count1 (car v) (car body) 0) 1)
  14662. (tree-memq (car v) inits)))
  14663. varlist)
  14664. (let ((new-cadr (copy (cadar body))))
  14665. (for-each (lambda (v)
  14666. (set! new-cadr (tree-subst (cadr v) (car v) new-cadr)))
  14667. varlist)
  14668. ;; (let ((a 1)) (do ((i a (+ i 1))) ((= i 3)) (display i))) -> (do ((i 1 (+ i 1))) ...)
  14669. (lint-format "perhaps ~A" caller
  14670. (lists->string form `(do ,new-cadr ...)))))))
  14671. ;; let->do -- sometimes a bad idea, set *max-cdr-len* to #f to disable this.
  14672. ;; (the main objection is that the s7/clm optimizer can't handle it, and
  14673. ;; instruments using it look kinda dumb -- the power of habit or something)
  14674. (when (integer? *max-cdr-len*)
  14675. (let ((inits (if (pair? (cadar body))
  14676. (map cadr (cadar body))
  14677. ()))
  14678. (locals (if (pair? (cadar body))
  14679. (map car (cadar body))
  14680. ())))
  14681. (unless (or (and (pair? inits)
  14682. (any? (lambda (v)
  14683. (or (memq (car v) locals) ; shadowing
  14684. (tree-memq (car v) inits)
  14685. (side-effect? (cadr v) env))) ; let var opens *stdin*, do stepper reads it at init
  14686. varlist))
  14687. (and (pair? (cdr body))
  14688. (pair? (cddr body)))
  14689. ;; moving more than one expr here is usually ugly -- the only exception I've
  14690. ;; seen is where the do body is enormous and the end stuff very short, and
  14691. ;; it (the end stuff) refers to the let/do variables -- in the unedited case,
  14692. ;; the result is hard to see.
  14693. (> (tree-leaves (cdr body)) *max-cdr-len*))
  14694. ;; (let ((xx 0)) (do ((x 1 (+ x 1)) (y x (- y 1))) ((= x 3) xx) (display y))) ->
  14695. ;; (do ((xx 0) (x 1 (+ x 1)) (y x (- y 1))) ...)
  14696. (lint-format "perhaps ~A" caller
  14697. (lists->string form
  14698. (let ((do-form (cdar body)))
  14699. (if (null? (cdr body)) ; do is only expr in let
  14700. `(do ,(append varlist (car do-form))
  14701. ...)
  14702. `(do ,(append varlist (car do-form))
  14703. (,(and (pair? (cadr do-form)) (caadr do-form))
  14704. ,@(if (side-effect? (cdadr do-form) env) (cdadr do-form) ())
  14705. ,@(cdr body)) ; include rest of let as do return value
  14706. ...)))))))))
  14707. (when (and (> (length body) 3) ; setting this to 1 did not catch anything new
  14708. (every? pair? varlist)
  14709. (not (tree-set-car-member '(define define* define-macro define-macro*
  14710. define-bacro define-bacro* define-constant define-expansion)
  14711. body)))
  14712. ;; define et al are like a continuation of the let bindings, so we can't restrict them by accident
  14713. ;; (let ((x 1)) (define y x) ...)
  14714. (let ((last-refs (map (lambda (v)
  14715. (vector (var-name v) #f 0 v))
  14716. vars))
  14717. (got-lambdas (tree-set-car-member '(lambda lambda*) body)))
  14718. ;; (let ((x #f) (y #t)) (set! x (lambda () y)) (set! y 5) (x))
  14719. (do ((p body (cdr p))
  14720. (i 0 (+ i 1)))
  14721. ((null? p)
  14722. (let ((end 0))
  14723. (for-each (lambda (v)
  14724. (set! end (max end (v 2))))
  14725. last-refs)
  14726. (if (and (< end (/ i lint-let-reduction-factor))
  14727. (eq? form lint-current-form)
  14728. (< (tree-leaves (car body)) 100))
  14729. (let ((old-start (let ((old-pp ((funclet lint-pretty-print) '*pretty-print-left-margin*)))
  14730. (set! ((funclet lint-pretty-print) '*pretty-print-left-margin*) (+ lint-left-margin 4))
  14731. (let ((res (lint-pp `(let ,(cadr form)
  14732. ,@(copy body (make-list (+ end 1)))))))
  14733. (set! ((funclet lint-pretty-print) '*pretty-print-left-margin*) old-pp)
  14734. res))))
  14735. (lint-format "this let could be tightened:~%~NC~A ->~%~NC~A~%~NC~A ..." caller
  14736. (+ lint-left-margin 4) #\space
  14737. (truncated-list->string form)
  14738. (+ lint-left-margin 4) #\space
  14739. old-start
  14740. (+ lint-left-margin 4) #\space
  14741. (lint-pp (list-ref body (+ end 1)))))
  14742. (begin
  14743. ;; look for bindings that can be severely localized
  14744. (let ((locals (map (lambda (v)
  14745. (if (and (integer? (v 1))
  14746. (< (- (v 2) (v 1)) 2)
  14747. (code-constant? (var-initial-value (v 3))))
  14748. v
  14749. (values)))
  14750. last-refs)))
  14751. ;; should this omit cases where most the let is in the one or two lines?
  14752. (when (pair? locals)
  14753. (set! locals (sort! locals (lambda (a b)
  14754. (or (< (a 1) (b 1))
  14755. (< (a 2) (b 2))))))
  14756. (do ((lv locals (cdr lv)))
  14757. ((null? lv))
  14758. (let* ((v (car lv))
  14759. (cur-line (v 1)))
  14760. (let gather ((pv lv) (cur-vars ()) (max-line (v 2)))
  14761. (if (or (null? (cdr pv))
  14762. (not (= cur-line ((cadr pv) 1))))
  14763. (begin
  14764. (set! cur-vars (reverse (cons (car pv) cur-vars)))
  14765. (set! max-line (max max-line ((car pv) 2)))
  14766. (set! lv pv)
  14767. (lint-format "~{~A~^, ~} ~A only used in expression~A (of ~A),~%~NC~A~A of~%~NC~A" caller
  14768. (map (lambda (v) (v 0)) cur-vars)
  14769. (if (null? (cdr cur-vars)) "is" "are")
  14770. (format #f (if (= cur-line max-line)
  14771. (values " ~D" (+ cur-line 1))
  14772. (values "s ~D and ~D" (+ cur-line 1) (+ max-line 1))))
  14773. (length body)
  14774. (+ lint-left-margin 6) #\space
  14775. (truncated-list->string (list-ref body cur-line))
  14776. (if (= cur-line max-line)
  14777. ""
  14778. (format #f "~%~NC~A"
  14779. (+ lint-left-margin 6) #\space
  14780. (truncated-list->string (list-ref body max-line))))
  14781. (+ lint-left-margin 4) #\space
  14782. (truncated-list->string form)))
  14783. (gather (cdr pv)
  14784. (cons (car pv) cur-vars)
  14785. (max max-line ((car pv) 2)))))))))
  14786. (let ((mnv ())
  14787. (cur-end i))
  14788. (for-each (lambda (v)
  14789. (when (and (or (null? mnv)
  14790. (<= (v 2) cur-end))
  14791. (positive? (var-ref (v 3)))
  14792. (let ((expr (var-initial-value (v 3))))
  14793. (not (any? (lambda (ov) ; watch out for shadowed vars
  14794. (tree-memq (car ov) expr))
  14795. varlist))))
  14796. (set! mnv (if (= (v 2) cur-end)
  14797. (cons v mnv)
  14798. (list v)))
  14799. (set! cur-end (v 2))))
  14800. last-refs)
  14801. ;; look for vars used only at the start of the let
  14802. (when (and (pair? mnv)
  14803. (< cur-end (/ i lint-let-reduction-factor))
  14804. (> (- i cur-end) 3))
  14805. ;; mnv is in the right order because last-refs is reversed
  14806. (lint-format "the scope of ~{~A~^, ~} could be reduced: ~A" caller
  14807. (map (lambda (v) (v 0)) mnv)
  14808. (lists->string form
  14809. `(let ,(map (lambda (v)
  14810. (if (member (car v) mnv (lambda (a b) (eq? a (b 0))))
  14811. (values)
  14812. v))
  14813. varlist)
  14814. (let ,(map (lambda (v)
  14815. (list (v 0) (var-initial-value (v 3))))
  14816. mnv)
  14817. ,@(copy body (make-list (+ cur-end 1))))
  14818. ,(list-ref body (+ cur-end 1))
  14819. ...)))))))))
  14820. ;; body of do loop above
  14821. (if (and (not got-lambdas)
  14822. (pair? (car p))
  14823. (pair? (cdr p))
  14824. (eq? (caar p) 'set!)
  14825. (var-member (cadar p) vars)
  14826. (not (tree-memq (cadar p) (cdr p))))
  14827. (if (not (side-effect? (caddar p) env)) ; (set! v0 (channel->vct 1000 100)) -> (channel->vct 1000 100)
  14828. (lint-format "~A in ~A could be omitted" caller (car p) (truncated-list->string form))
  14829. (lint-format "perhaps ~A" caller (lists->string (car p) (caddar p)))))
  14830. ;; 1 use in cadr and none thereafter happens a few times, but looks like set-as-documentation mostly
  14831. (for-each (lambda (v)
  14832. (when (tree-memq (v 0) (car p))
  14833. (set! (v 2) i)
  14834. (if (not (v 1)) (set! (v 1) i))))
  14835. last-refs))))))
  14836. ) ; (when (pair? body)...)
  14837. ;; out of place and repetitive code...
  14838. (when (and (pair? (cadr form))
  14839. (pair? (cddr form))
  14840. (null? (cdddr form))
  14841. (pair? (caddr form)))
  14842. (let ((inner (caddr form)) ; the inner let
  14843. (outer-vars (cadr form)))
  14844. (when (pair? (cdr inner))
  14845. (let ((inner-vars (cadr inner)))
  14846. (when (and (eq? (car inner) 'let)
  14847. (symbol? inner-vars))
  14848. (let ((named-body (cdddr inner))
  14849. (named-args (caddr inner)))
  14850. (unless (any? (lambda (v)
  14851. (or (not (= (tree-count1 (car v) named-args 0) 1))
  14852. (tree-memq (car v) named-body)))
  14853. varlist)
  14854. (let ((new-args (copy named-args)))
  14855. (for-each (lambda (v)
  14856. (set! new-args (tree-subst (cadr v) (car v) new-args)))
  14857. varlist)
  14858. ;; (let ((x 1) (y (f g 2))) (let loop ((a (+ x 1)) (b y)) (loop a b))) -> (let loop ((a (+ 1 1)) (b (f g 2))) (loop a b))
  14859. (lint-format "perhaps ~A" caller
  14860. (lists->string form
  14861. `(let ,inner-vars ,new-args ,@named-body)))))))
  14862. ;; maybe more code than this is worth -- combine lets
  14863. (when (and (memq (car inner) '(let let*))
  14864. (pair? inner-vars))
  14865. (define (letstar . lets)
  14866. (let loop ((vars (list 'curlet)) (forms lets))
  14867. (and (pair? forms)
  14868. (or (and (pair? (car forms))
  14869. (or (tree-set-member vars (car forms))
  14870. (any? (lambda (a)
  14871. (or (not (pair? a))
  14872. (not (pair? (cdr a)))
  14873. (side-effect? (cadr a) env)))
  14874. (car forms))))
  14875. (loop (append (map car (car forms)) vars)
  14876. (cdr forms))))))
  14877. (cond ((and (null? (cdadr form)) ; let(1) + let* -> let*
  14878. (eq? (car inner) 'let*)
  14879. (not (symbol? inner-vars))) ; not named let*
  14880. ;; (let ((a 1)) (let* ((b (+ a 1)) (c (* b 2))) (display (+ a b c)))) -> (let* ((a 1) (b (+ a 1)) (c (* b 2))) (display (+ a b c)))
  14881. (lint-format "perhaps ~A" caller
  14882. (lists->string form
  14883. `(let* ,(append outer-vars inner-vars)
  14884. ,@(one-call-and-dots (cddr inner))))))
  14885. ((and (pair? (cddr inner))
  14886. (pair? (caddr inner))
  14887. (null? (cdddr inner))
  14888. (eq? (caaddr inner) 'let)
  14889. (pair? (cdr (caddr inner)))
  14890. (pair? (cadr (caddr inner))))
  14891. (let* ((inner1 (cdaddr inner))
  14892. (inner1-vars (car inner1)))
  14893. (if (and (pair? (cdr inner1))
  14894. (null? (cddr inner1))
  14895. (pair? (cadr inner1))
  14896. (eq? (caadr inner1) 'let)
  14897. (pair? (cdadr inner1))
  14898. (pair? (cadadr inner1)))
  14899. (let* ((inner2 (cdadr inner1))
  14900. (inner2-vars (car inner2)))
  14901. (if (not (letstar outer-vars
  14902. inner-vars
  14903. inner1-vars
  14904. inner2-vars))
  14905. ;; (let ((a 1)) (let ((b 2)) (let ((c 3)) (let ((d 4)) (+ a b c d))))) -> (let ((a 1) (b 2) (c 3) (d 4)) (+ a b c d))
  14906. (lint-format "perhaps ~A" caller
  14907. (lists->string form
  14908. `(let ,(append outer-vars inner-vars inner1-vars inner2-vars)
  14909. ,@(one-call-and-dots (cdr inner2)))))))
  14910. (if (not (letstar outer-vars
  14911. inner-vars
  14912. inner1-vars))
  14913. ;; (let ((b 2)) (let ((c 3)) (let ((d 4)) (+ a b c d)))) -> (let ((b 2) (c 3) (d 4)) (+ a b c d))
  14914. (lint-format "perhaps ~A" caller
  14915. (lists->string form
  14916. `(let ,(append outer-vars inner-vars inner1-vars)
  14917. ,@(one-call-and-dots (cdr inner1)))))))))
  14918. ((not (letstar outer-vars
  14919. inner-vars))
  14920. ;; (let ((c 3)) (let ((d 4)) (+ a b c d))) -> (let ((c 3) (d 4)) (+ a b c d))
  14921. (lint-format "perhaps ~A" caller
  14922. (lists->string form
  14923. `(let ,(append outer-vars inner-vars)
  14924. ,@(one-call-and-dots (cddr inner))))))
  14925. ((and (null? (cdadr form)) ; 1 outer var
  14926. (pair? inner-vars)
  14927. (null? (cdadr inner))) ; 1 inner var, dependent on outer
  14928. ;; (let ((x 0)) (let ((y (g 0))) (+ x y))) -> (let* ((x 0) (y (g 0))) (+ x y))
  14929. (lint-format "perhaps ~A" caller
  14930. (lists->string form
  14931. `(let* ,(append outer-vars inner-vars)
  14932. ,@(one-call-and-dots (cddr inner))))))))))))
  14933. ))) ; messed up let
  14934. env)
  14935. (hash-table-set! h 'let let-walker))
  14936. ;; ---------------- let* ----------------
  14937. (let ()
  14938. (define (let*-walker caller form env)
  14939. (if (< (length form) 3)
  14940. (lint-format "let* is messed up: ~A" caller (truncated-list->string form))
  14941. (let ((named-let (and (symbol? (cadr form)) (cadr form))))
  14942. (let ((vars (if named-let (list (make-var :name named-let
  14943. :definer 'let*)) ()))
  14944. (varlist ((if named-let caddr cadr) form))
  14945. (body ((if named-let cdddr cddr) form)))
  14946. (if (not (list? varlist))
  14947. (lint-format "let* is messed up: ~A" caller (truncated-list->string form)))
  14948. ;; let->do (could go further down)
  14949. (when (and (integer? *max-cdr-len*)
  14950. (pair? varlist)
  14951. (pair? body)
  14952. (pair? (car body))
  14953. (eq? (caar body) 'do)
  14954. (< (tree-leaves (cdr body)) *max-cdr-len*))
  14955. (let ((inits (if (pair? (cadar body))
  14956. (map cadr (cadar body))
  14957. ()))
  14958. (locals (if (pair? (cadar body))
  14959. (map car (cadar body))
  14960. ()))
  14961. (lv (list-ref varlist (- (length varlist) 1))))
  14962. (unless (and (pair? inits)
  14963. (or (memq (car lv) locals) ; shadowing
  14964. (tree-memq (car lv) inits)
  14965. (side-effect? (cadr lv) env)))
  14966. ;; (let* ((x (log z))) (do ((i 0 (+ x z))) ((= i 3)) (display x))) -> (do ((x (log z)) (i 0 (+ x z))) ...)
  14967. (lint-format "perhaps ~A" caller
  14968. (lists->string form
  14969. (let ((new-do (let ((do-form (cdar body)))
  14970. (if (null? (cdr body))
  14971. `(do ,(cons lv (car do-form))
  14972. ...)
  14973. `(do ,(cons lv (car do-form))
  14974. (,(and (pair? (cadr do-form)) (caadr do-form))
  14975. ,@(if (side-effect? (cdadr do-form) env) (cdadr do-form) ())
  14976. ,@(cdr body)) ; include rest of let as do return value
  14977. ...)))))
  14978. (case (length varlist)
  14979. ((1) new-do)
  14980. ((2) `(let (,(car varlist)) ,new-do))
  14981. (else `(let* ,(copy varlist (make-list (- (length varlist) 1)))
  14982. ,new-do)))))))))
  14983. (do ((side-effects #f)
  14984. (bindings varlist (cdr bindings)))
  14985. ((not (pair? bindings))
  14986. (if (not (null? bindings))
  14987. (lint-format "let* variable list is not a proper list? ~S"
  14988. caller ((if named-let caddr cadr) form)))
  14989. (if (not (or side-effects
  14990. (any? (lambda (v) (positive? (var-ref v))) vars)))
  14991. ;; (let* ((x (log y))) x)
  14992. (lint-format "let* could be let: ~A" caller (truncated-list->string form))))
  14993. ;; in s7, let evaluates var values top down, so this message is correct
  14994. ;; even in cases like (let ((ind (open-sound...)) (mx (maxamp))) ...)
  14995. ;; in r7rs, the order is not specified (section 4.2.2 of the spec), so
  14996. ;; here we would restrict this message to cases where there is only
  14997. ;; one variable, or where subsequent values are known to be independent.
  14998. ;; if each function could tell us what globals it depends on or affects,
  14999. ;; we could make this work in all cases.
  15000. (when (binding-ok? caller 'let* (car bindings) env #f)
  15001. (let ((expr (cadar bindings))
  15002. (side (side-effect? (cadar bindings) env)))
  15003. (if (not (or (eq? bindings varlist)
  15004. ;; first var side-effect is innocuous (especially if it's the only one!)
  15005. ;; does this need to protect against a side-effect that the next var accesses?
  15006. ;; I think we're ok -- the accessed var must be exterior, and we go down in order
  15007. side-effects))
  15008. (set! side-effects side))
  15009. (let ((e (lint-walk caller expr (append vars env))))
  15010. (if (and (pair? e)
  15011. (not (eq? e env))
  15012. (memq (var-name (car e)) '(:lambda :dilambda)))
  15013. (let ((ldata (cdar e)))
  15014. (set! (var-name (car e)) (caar bindings))
  15015. (set! (ldata 'initial-value) expr)
  15016. (set! vars (cons (car e) vars)))
  15017. (set! vars (cons (make-var :name (caar bindings)
  15018. :initial-value expr
  15019. :definer (if named-let 'named-let* 'let*))
  15020. vars))))
  15021. ;; look for duplicate values
  15022. ;; someday protect against any shadows if included in any expr
  15023. (unless (or side
  15024. (not (pair? expr))
  15025. (code-constant? expr)
  15026. (maker? expr))
  15027. (let ((name (caar bindings)))
  15028. (let dup-check ((vs (cdr vars)))
  15029. (if (and (pair? vs)
  15030. (pair? (car vs))
  15031. (not (eq? name (caar vs)))
  15032. (not (tree-memq (caar vs) expr)))
  15033. ;; perhaps also not side-effect of car vs initial-value (char-ready? + read + char-ready? again)
  15034. (if (equal? expr (var-initial-value (car vs)))
  15035. ;; (let* ((x (log y 2)) (y (log y 2)) (z (f x))) (+ x y z z))
  15036. (lint-format "~A's value ~A could be ~A" caller
  15037. name expr (caar vs))
  15038. (dup-check (cdr vs))))))))))
  15039. ;; if var is not used except in other var bindings, it can be moved out of this let*
  15040. ;; collect vars not in body, used in only one binding, gather these cases, and rewrite the let*
  15041. ;; repeated names are possible here
  15042. ;; also cascading dependencies: (let* ((z 1) (y (+ z 2)) (x (< y 3))) (if x (f x)))
  15043. ;; (let ((x (let ((y (let ((z 1))) (+ z 2))) (< y 3)))) ...) ??
  15044. ;; new-vars: ((z y) (y x))
  15045. (when (and (pair? vars)
  15046. (pair? (cdr vars)))
  15047. (let ((new-vars ())
  15048. (vs-pos vars)
  15049. (repeats (do ((p vars (cdr p)))
  15050. ((or (null? p)
  15051. (var-member (var-name (car p)) (cdr p)))
  15052. (pair? p)))))
  15053. (for-each (lambda (v)
  15054. (let ((vname (var-name v))
  15055. (vvalue #f))
  15056. (if (not (tree-memq vname body))
  15057. (let walker ((vs vars))
  15058. (if (not (pair? vs))
  15059. (if (and vvalue
  15060. (or (not (side-effect? (var-initial-value v) env))
  15061. (eq? vvalue (var-name (car vs-pos)))))
  15062. (set! new-vars (cons (list vvalue vname (var-initial-value v)) new-vars)))
  15063. (let ((b (car vs)))
  15064. (if (or (eq? (var-name b) vname)
  15065. (not (tree-memq vname (var-initial-value b)))) ; tree-memq matches the bare symbol (tree-member doesn't)
  15066. (walker (cdr vs))
  15067. (if (not vvalue)
  15068. (begin
  15069. (set! vvalue (var-name b))
  15070. (walker (cdr vs)))))))))
  15071. (set! vs-pos (cdr vs-pos))))
  15072. (cdr vars)) ; vars is reversed from code order, new-vars is in code order
  15073. (when (pair? new-vars)
  15074. (define (gather-dependencies var val env)
  15075. (let ((deps ()))
  15076. (for-each (lambda (nv)
  15077. (if (and (eq? (car nv) var)
  15078. (or (not repeats)
  15079. (tree-memq (cadr nv) val)))
  15080. (set! deps (cons (list (cadr nv)
  15081. (gather-dependencies (cadr nv) (caddr nv) env))
  15082. deps))))
  15083. new-vars)
  15084. (if (> (tree-leaves val) 30)
  15085. (set! val '...))
  15086. (if (pair? deps)
  15087. `(,(if (null? (cdr deps)) 'let 'let*)
  15088. ,deps ,val)
  15089. val)))
  15090. (let ((new-let-binds (map (lambda (v)
  15091. (if (member (var-name v) new-vars (lambda (name lst) (eq? name (cadr lst))))
  15092. (values)
  15093. `(,(var-name v) ,(gather-dependencies (var-name v) (var-initial-value v) env))))
  15094. (reverse vars))))
  15095. ;; (let* ((a 1) (b 2) (c (+ a 1))) (* c 2)) -> (let* ((b 2) (c (let ((a 1)) (+ a 1)))) ...)
  15096. (lint-format "perhaps restrict ~{~A~^, ~} which ~A not used in the let* body ~A" caller
  15097. (map cadr new-vars)
  15098. (if (null? (cdr new-vars)) "is" "are")
  15099. (lists->string form
  15100. `(,(if (null? (cdr new-let-binds))
  15101. 'let 'let*)
  15102. ,new-let-binds
  15103. ...)))))
  15104. ;; this could be folded into the for-each above
  15105. (unless repeats
  15106. (let ((outer-vars ())
  15107. (inner-vars ()))
  15108. (do ((vs (reverse vars) (cdr vs)))
  15109. ((null? vs))
  15110. (let* ((v (car vs))
  15111. (vname (var-name v)))
  15112. (if (not (or (side-effect? (var-initial-value v) env)
  15113. (any? (lambda (trailing-var)
  15114. ;; vname is possible inner let var if it is not mentioned in any trailing initial value
  15115. ;; (repeated name can't happen here)
  15116. (tree-memq vname (var-initial-value trailing-var)))
  15117. (cdr vs))))
  15118. (set! inner-vars (cons v inner-vars))
  15119. (set! outer-vars (cons v outer-vars)))))
  15120. (when (and (pair? outer-vars)
  15121. (pair? inner-vars)
  15122. (pair? (cdr inner-vars)))
  15123. ;; (let* ((a 1) (b 2) (c (+ a 1))) (* c 2)) -> (let ((a 1)) (let ((b 2) (c (+ a 1))) ...))
  15124. (lint-format "perhaps split this let*: ~A" caller
  15125. (lists->string form
  15126. `(,(if (pair? (cdr outer-vars)) 'let* 'let)
  15127. ,(map (lambda (v)
  15128. `(,(var-name v) ,(var-initial-value v)))
  15129. (reverse outer-vars))
  15130. (let ,(map (lambda (v)
  15131. `(,(var-name v) ,(var-initial-value v)))
  15132. (reverse inner-vars))
  15133. ...)))))))
  15134. )) ; pair? vars
  15135. (let* ((cur-env (cons (make-var :name :let
  15136. :initial-value form
  15137. :definer 'let*)
  15138. (append vars env)))
  15139. (e (lint-walk-body caller 'let* body cur-env)))
  15140. (let ((nvars (and (not (eq? e cur-env))
  15141. (env-difference caller e cur-env ()))))
  15142. (if (pair? nvars)
  15143. (if (memq (var-name (car nvars)) '(:lambda :dilambda))
  15144. (begin
  15145. (set! env (cons (car nvars) env))
  15146. (set! nvars (cdr nvars)))
  15147. (set! vars (append nvars vars)))))
  15148. (report-usage caller 'let* vars e))
  15149. (when (and (not named-let)
  15150. (pair? body)
  15151. (pair? varlist)) ; from here to end
  15152. ;; (let*->let*) combined into one
  15153. (when (and (pair? (car body))
  15154. (or (eq? (caar body) 'let*) ; let*+let* -> let*
  15155. (and (eq? (caar body) 'let) ; let*+let(1) -> let*
  15156. (or (null? (cadar body))
  15157. (and (pair? (cadar body))
  15158. (null? (cdadar body))))))
  15159. (null? (cdr body))
  15160. (not (symbol? (cadar body))))
  15161. ;; (let* ((a 1) (b (+ a 2))) (let* ((c (+ b 3)) (d (+ c 4))) (display a) (+ a... ->
  15162. ;; (let* ((a 1) (b (+ a 2)) (c (+ b 3)) (d (+ c 4))) (display a) ...)
  15163. (lint-format "perhaps ~A" caller
  15164. (lists->string form
  15165. `(let* ,(append varlist (cadar body))
  15166. ,@(one-call-and-dots (cddar body))))))
  15167. (when (and (proper-list? (cadr form))
  15168. (not (tree-set-member '(curlet lambda lambda* define define*) (cddr form))))
  15169. ;; see let above
  15170. (do ((changes ())
  15171. (vs (cadr form) (cdr vs)))
  15172. ((null? vs)
  15173. (if (pair? changes)
  15174. (let ((new-form (copy form)))
  15175. (for-each
  15176. (lambda (v)
  15177. (list-set! new-form 1 (remove-if (lambda (p) (equal? p v)) (cadr new-form)))
  15178. (set! new-form (tree-subst (cadr v) (car v) new-form)))
  15179. changes)
  15180. ;; (let* ((x y) (a (* 2 x))) (+ (f a (+ a 1)) (* 3 x))) -> (let ((a (* 2 y))) (+ (f a (+ a 1)) (* 3 y)))
  15181. (lint-format "assuming we see all set!s, the binding~A ~{~A~^, ~} ~A pointless: perhaps ~A" caller
  15182. (if (pair? (cdr changes)) "s" "")
  15183. changes
  15184. (if (pair? (cdr changes)) "are" "is")
  15185. (lists->string form
  15186. (let ((header (if (and (pair? (cadr new-form))
  15187. (pair? (cdadr new-form)))
  15188. 'let* 'let)))
  15189. (if (< (tree-leaves new-form) 200)
  15190. `(,header ,@(cdr new-form))
  15191. `(,header ,(cadr new-form)
  15192. ,@(one-call-and-dots (cddr new-form))))))))))
  15193. (let ((v (car vs)))
  15194. (if (and (pair? v)
  15195. (pair? (cdr v))
  15196. (null? (cddr v))
  15197. (symbol? (cadr v))
  15198. (not (assq (cadr v) (cadr form))) ; value is not a local var
  15199. (not (set-target (car v) body env))
  15200. (not (set-target (cadr v) body env)))
  15201. (let ((data (var-member (cadr v) env)))
  15202. (if (and (or (not (var? data))
  15203. (and (not (eq? (var-definer data) 'parameter))
  15204. (or (null? (var-setters data))
  15205. (not (tree-set-member (var-setters data) body)))))
  15206. (not (any? (lambda (p)
  15207. (and (pair? p)
  15208. (pair? (cdr p))
  15209. (or (set-target (cadr v) (cdr p) env)
  15210. (set-target (car v) (cdr p) env)
  15211. (and (var? data)
  15212. (pair? (var-setters data))
  15213. (tree-set-member (var-setters data) body)))))
  15214. (cdr vs))))
  15215. (set! changes (cons v changes))))))))
  15216. (let* ((varlist-len (length varlist))
  15217. (last-var (and (positive? varlist-len)
  15218. (list-ref varlist (- varlist-len 1))))) ; from here to end
  15219. (when (pair? last-var) ; successive vars, first used in second but nowhere else -- combine if (very!) simple-looking
  15220. (do ((gone-vars ())
  15221. (v varlist (cdr v)))
  15222. ((or (null? v)
  15223. (null? (cdr v)))
  15224. (when (pair? gone-vars)
  15225. (let ((waiter #f)
  15226. (new-vars ())
  15227. (save-vars ()))
  15228. (set! gone-vars (reverse gone-vars))
  15229. (set! new-vars (map (lambda (v)
  15230. (if (and (pair? gone-vars)
  15231. (eq? v (car gone-vars)))
  15232. (begin
  15233. (set! waiter v)
  15234. (set! gone-vars (cdr gone-vars))
  15235. (values))
  15236. (if (not waiter)
  15237. v
  15238. (let ((new-v (tree-subst (cadr waiter) (car waiter) v)))
  15239. (set! save-vars (cons (list (car waiter) (car v)) save-vars))
  15240. (set! waiter #f)
  15241. new-v))))
  15242. varlist))
  15243. ;; (let* ((y 3) (x (log y))) x) -> (let ((x (log 3))) ...)
  15244. (lint-format "perhaps substitute ~{~{~A into ~A~}~^, ~}: ~A" caller
  15245. (reverse save-vars)
  15246. (lists->string form
  15247. `(,(if (null? (cdr new-vars)) 'let 'let*)
  15248. ,new-vars
  15249. ...))))))
  15250. (let ((cur-var (car v))
  15251. (nxt-var (cadr v)))
  15252. (when (and (pair? cur-var)
  15253. (let ((v (var-member (car cur-var) vars)))
  15254. (and (var? v)
  15255. (zero? (var-set v))))
  15256. (pair? nxt-var)
  15257. (pair? (cdr cur-var))
  15258. (pair? (cdr nxt-var))
  15259. (< (tree-leaves (cadr cur-var)) 8)
  15260. (not (and (pair? (cadr nxt-var))
  15261. (eq? (caadr nxt-var) 'let) ; if named-let, forget it
  15262. (pair? (cdadr nxt-var))
  15263. (symbol? (cadadr nxt-var))))
  15264. (or (not (pair? (cadr nxt-var)))
  15265. (not (side-effect? (cadr cur-var) env))
  15266. (every? (lambda (a)
  15267. (or (code-constant? a)
  15268. (assq a varlist)))
  15269. (cdadr nxt-var)))
  15270. (= (tree-count1 (car cur-var) (cadr nxt-var) 0) 1)
  15271. (not (tree-memq (car cur-var) (cddr v)))
  15272. (not (tree-memq (car cur-var) body)))
  15273. (set! gone-vars (cons cur-var gone-vars))
  15274. (set! v (cdr v)))))
  15275. ;; if last var only occurs once in body, and timing can't be an issue, substitute its value
  15276. ;; this largely copied from the let case above (but only one substitution)
  15277. ;; in both cases, we're assuming that the possible last-var value's side-effect won't
  15278. ;; affect other vars (in let* the local, in let something outside that might be used locally)
  15279. ;; perhaps add (not (side-effect (cadr last-var) env))?
  15280. (when (and (pair? (cdr last-var)) ; varlist-len can be 1 here
  15281. (< (tree-leaves (cadr last-var)) 12)
  15282. (= (tree-count1 (car last-var) body 0) 1)
  15283. (pair? (car body))
  15284. (null? (cdr body))
  15285. (not (memq (caar body) '(lambda lambda* define define* define-macro)))
  15286. (not (and (eq? (caar body) 'set!)
  15287. (eq? (car last-var) (cadar body))))
  15288. (not (any-macro? (caar body) env))
  15289. (not (any? (lambda (p)
  15290. (and (pair? p)
  15291. (not (eq? (car p) 'quote))
  15292. (or (not (hash-table-ref no-side-effect-functions (car p)))
  15293. (any? pair? (cdr p)))))
  15294. (cdar body))))
  15295. ;; (let* ((a 1) (b 2) (c (+ a 1))) (* c 2)) -> (let* ((a 1) (b 2)) (* (+ a 1) 2))
  15296. (lint-format "perhaps ~A" caller
  15297. (lists->string form `(,(if (<= varlist-len 2) 'let 'let*)
  15298. ,(copy varlist (make-list (- varlist-len 1)))
  15299. ,@(tree-subst (cadr last-var) (car last-var) body)))))
  15300. (when (null? (cdr body)) ; (let* (...(x A)) (if x (f A) B)) -> (let(*) (...) (cond (A => f) (else B)))
  15301. (when (pair? (cdr last-var))
  15302. (let ((p (car body)))
  15303. (when (and (pair? p)
  15304. (pair? (cdr p))
  15305. (case (car p)
  15306. ((if and) (eq? (cadr p) (car last-var)))
  15307. ((or) (equal? (cadr p) `(not ,(car last-var))))
  15308. (else #f))
  15309. (pair? (cddr p))
  15310. (pair? (caddr p))
  15311. (or (eq? (car p) 'if)
  15312. (null? (cdddr p)))
  15313. (pair? (cdaddr p))
  15314. (not (eq? (caaddr p) (car last-var))) ; ! (let* (...(x A)) (if x (x x)))
  15315. (null? (cddr (caddr p)))
  15316. (eq? (car last-var) (cadr (caddr p))))
  15317. (let ((else-clause (if (pair? (cdddr p)) ; only if 'if (see above)
  15318. (if (eq? (cadddr p) (car last-var))
  15319. `((else #f)) ; this stands in for the local var
  15320. (if (and (pair? (cadddr p))
  15321. (tree-unquoted-member (car last-var) (cadddr p)))
  15322. :oops! ; if the let var appears in the else portion, we can't do anything with =>
  15323. `((else ,(cadddr p)))))
  15324. (case (car p)
  15325. ((and) '((else #f)))
  15326. ((or) '((else #t)))
  15327. (else ())))))
  15328. (unless (eq? else-clause :oops!)
  15329. ;; (let* ((x (f y))) (and x (g x))) -> (cond ((f y) => g) (else #f)
  15330. (lint-format "perhaps ~A" caller
  15331. (case varlist-len
  15332. ((1) (lists->string form
  15333. `(cond (,(cadr last-var) => ,(caaddr p)) ,@else-clause)))
  15334. ((2) (lists->string form
  15335. `(let (,(car varlist))
  15336. (cond (,(cadr last-var) => ,(caaddr p)) ,@else-clause))))
  15337. (else (lists->string form
  15338. `(let* ,(copy varlist (make-list (- varlist-len 1)))
  15339. (cond (,(cadr last-var) => ,(caaddr p)) ,@else-clause)))))))))))
  15340. (when (and (pair? (car varlist)) ; same as let: (let* ((x y)) x) -> y -- (let* (x) ...)
  15341. (not (pair? (car body))))
  15342. (if (and (eq? (car body) (caar varlist))
  15343. (null? (cdr varlist))
  15344. (pair? (cdar varlist))) ; (let* ((a...)) a)
  15345. ;; (let* ((x (log y))) x) -> (log y)
  15346. (lint-format "perhaps ~A" caller (lists->string form (cadar varlist)))
  15347. (if (and (> varlist-len 1) ; (let* (... (x y)) x) -> (let(*)(...) y)
  15348. (pair? last-var)
  15349. (pair? (cdr last-var))
  15350. (null? (cddr last-var))
  15351. (eq? (car body) (car last-var)))
  15352. ;; (let* ((y 3) (x (log y))) x) -> (let ((y 3)) (log y))
  15353. (lint-format "perhaps ~A" caller
  15354. (lists->string form `(,(if (= varlist-len 2) 'let 'let*)
  15355. ,(copy varlist (make-list (- varlist-len 1)))
  15356. ,(cadr last-var)))))))))
  15357. (when (and (> (length body) 3)
  15358. (> (length vars) 1)
  15359. (every? pair? varlist)
  15360. (not (tree-set-car-member '(define define* define-macro define-macro*
  15361. define-bacro define-bacro* define-constant define-expansion)
  15362. body)))
  15363. (let ((last-ref (vector (var-name (car vars)) #f 0 (car vars))))
  15364. (do ((p body (cdr p))
  15365. (i 0 (+ i 1)))
  15366. ((null? p)
  15367. (let ((cur-line (last-ref 1))
  15368. (max-line (last-ref 2))
  15369. (vname (last-ref 0)))
  15370. (if (and (< max-line (/ i lint-let-reduction-factor))
  15371. (> (- i max-line) 3))
  15372. (lint-format "the scope of ~A could be reduced: ~A" caller
  15373. vname
  15374. (lists->string form
  15375. `(,(if (> (length vars) 2) 'let* 'let)
  15376. ,(copy varlist (make-list (- (length vars) 1)))
  15377. (let (,(list vname (var-initial-value (last-ref 3))))
  15378. ,@(copy body (make-list (+ max-line 1))))
  15379. ,(list-ref body (+ max-line 1))
  15380. ...)))
  15381. (when (and (integer? cur-line)
  15382. (< (- max-line cur-line) 2)
  15383. (code-constant? (var-initial-value (last-ref 3))))
  15384. (lint-format "~A is only used in expression~A (of ~A),~%~NC~A~A of~%~NC~A" caller
  15385. vname
  15386. (format #f (if (= cur-line max-line)
  15387. (values " ~D" (+ cur-line 1))
  15388. (values "s ~D and ~D" (+ cur-line 1) (+ max-line 1))))
  15389. (length body)
  15390. (+ lint-left-margin 6) #\space
  15391. (truncated-list->string (list-ref body cur-line))
  15392. (if (= cur-line max-line)
  15393. ""
  15394. (format #f "~%~NC~A"
  15395. (+ lint-left-margin 6) #\space
  15396. (truncated-list->string (list-ref body max-line))))
  15397. (+ lint-left-margin 4) #\space
  15398. (truncated-list->string form))))))
  15399. (when (tree-memq (last-ref 0) (car p))
  15400. (set! (last-ref 2) i)
  15401. (if (not (last-ref 1)) (set! (last-ref 1) i))))))
  15402. )))))
  15403. env)
  15404. (hash-table-set! h 'let* let*-walker))
  15405. ;; ---------------- letrec ----------------
  15406. (let ()
  15407. (define (letrec-walker caller form env)
  15408. (if (< (length form) 3) ; (letrec () . 1)
  15409. (lint-format "~A is messed up: ~A" caller (car form) (truncated-list->string form))
  15410. (let ((vars ())
  15411. (head (car form)))
  15412. (cond ((null? (cadr form)) ; (letrec () 1)
  15413. (lint-format "~A could be let: ~A" caller head (truncated-list->string form)))
  15414. ((not (pair? (cadr form))) ; (letrec a b)
  15415. (lint-format "~A is messed up: ~A" caller head (truncated-list->string form)))
  15416. ((and (null? (cdadr form))
  15417. (eq? head 'letrec*)) ; (letrec* ((a (lambda b (a 1)))) a)
  15418. (lint-format "letrec* could be letrec: ~A" caller (truncated-list->string form))))
  15419. (do ((warned (or (eq? head 'letrec*)
  15420. (not (pair? (cadr form)))
  15421. (negative? (length (cadr form))))) ; malformed letrec
  15422. (baddy #f)
  15423. (bindings (cadr form) (cdr bindings)))
  15424. ((not (pair? bindings))
  15425. (if (not (null? bindings)) ; (letrec* letrec)!
  15426. (lint-format "~A variable list is not a proper list? ~S" caller head (cadr form))))
  15427. (when (and (not warned) ; letrec -> letrec*
  15428. (pair? (car bindings))
  15429. (pair? (cdar bindings))
  15430. ;; type of current var is not important -- if used in non-function elsewhere,
  15431. ;; it has to be letrec*
  15432. (any? (lambda (b)
  15433. (and (pair? b)
  15434. (pair? (cdr b))
  15435. (or (and (not (pair? (cadr b)))
  15436. (eq? (caar bindings) (cadr b)))
  15437. (tree-memq (caar bindings) (cadr b)))
  15438. (not (tree-set-member '(lambda lambda* define define* case-lambda) (cadr b)))
  15439. (set! baddy b)))
  15440. (cdr bindings)))
  15441. (set! warned #t)
  15442. ;; (letrec ((x 32) (f1 (let ((y 1)) (lambda (z) (+ x y z)))) (f2 (f1 x))) (+ x f2))
  15443. (lint-format "in ~A,~%~NCletrec should be letrec* because ~A is used in ~A's value (not a function): ~A" caller
  15444. (truncated-list->string form)
  15445. (+ lint-left-margin 4) #\space
  15446. (caar bindings)
  15447. (car baddy)
  15448. (cadr baddy)))
  15449. (when (binding-ok? caller head (car bindings) env #f)
  15450. (let ((init (if (and (eq? (caar bindings) (cadar bindings))
  15451. (or (eq? head 'letrec)
  15452. (not (var-member (caar bindings) vars))))
  15453. (begin ; (letrec ((x x)) x)
  15454. (lint-format "~A is the same as (~A #<undefined>) in ~A" caller
  15455. (car bindings) (caar bindings) head)
  15456. ;; in letrec* ((x 12) (x x)) is an error
  15457. #<undefined>)
  15458. (cadar bindings))))
  15459. (set! vars (cons (make-var :name (caar bindings)
  15460. :initial-value init
  15461. :definer head)
  15462. vars)))))
  15463. (when (eq? head 'letrec)
  15464. (check-unordered-exprs caller form (map var-initial-value vars) env))
  15465. (when (pair? vars)
  15466. (do ((bindings (cadr form) (cdr bindings)) ; if none of the local vars occurs in any of the values, no need for the "rec"
  15467. (vs (map var-name vars)))
  15468. ((or (not (pair? bindings))
  15469. (not (pair? (car bindings)))
  15470. (not (pair? (cdar bindings)))
  15471. (memq (cadar bindings) vs)
  15472. (tree-set-member vs (cadar bindings)))
  15473. (when (null? bindings)
  15474. (let ((letx (if (or (eq? head 'letrec)
  15475. (do ((p (map cadr (cadr form)) (cdr p))
  15476. (q (map car (cadr form)) (cdr q)))
  15477. ((or (null? p)
  15478. (side-effect? (car p) env)
  15479. (memq (car q) (cdr q)))
  15480. (null? p))))
  15481. 'let 'let*)))
  15482. ;; (letrec ((f1 (lambda (a) a))) 32)
  15483. (lint-format "~A could be ~A: ~A" caller
  15484. head letx
  15485. (truncated-list->string form))))))
  15486. (when (and (null? (cdr vars))
  15487. (pair? (cddr form))
  15488. (pair? (caddr form))
  15489. (null? (cdddr form)))
  15490. (let ((body (caddr form))
  15491. (sym (var-name (car vars)))
  15492. (lform (cadar (cadr form)))) ; the letrec var's lambda
  15493. (when (and (pair? lform)
  15494. (pair? (cdr lform))
  15495. (eq? (car lform) 'lambda)
  15496. (proper-list? (cadr lform))) ; includes ()
  15497. (if (eq? sym (car body)) ; (letrec ((x (lambda ...))) (x...)) -> (let x (...)...)
  15498. (if (and (not (tree-memq sym (cdr body)))
  15499. (< (tree-leaves body) 100))
  15500. ;; the limit on tree-leaves is for cases where the args are long lists of data --
  15501. ;; more like for-each than let, and easier to read if the code is first, I think.
  15502. (lint-format "perhaps ~A" caller
  15503. (lists->string
  15504. form `(let ,sym
  15505. ,(map list (cadr lform) (cdr body))
  15506. ,@(cddr lform)))))
  15507. (if (and (not (eq? caller 'define))
  15508. (= (tree-count1 sym body 0) 1))
  15509. (let ((call (find-call sym body)))
  15510. (when (pair? call)
  15511. (let ((new-call `(let ,sym
  15512. ,(map list (cadr lform) (cdr call))
  15513. ,@(cddr lform))))
  15514. (lint-format "perhaps ~A" caller
  15515. (lists->string form (tree-subst new-call call body))))))))))))
  15516. ;; maybe (let () ...) here because (letrec ((x (lambda (y) (+ y 1)))) (x (define z 32))) needs to block z?
  15517. ;; currently we get (let x ((y (define z 32))) (+ y 1))
  15518. ;; and even that should be (let () (define z 32) (+ z 1)) or something similar
  15519. ;; lambda here is handled under define??
  15520. (let ((new-env (append vars env)))
  15521. (when (pair? (cadr form))
  15522. (for-each (lambda (binding)
  15523. (if (binding-ok? caller head binding env #t)
  15524. (lint-walk caller (cadr binding) new-env)))
  15525. (cadr form)))
  15526. (let* ((cur-env (cons (make-var :name :let
  15527. :initial-value form
  15528. :definer head)
  15529. (append vars env)))
  15530. (e (lint-walk-body caller head (cddr form) cur-env)))
  15531. (let ((nvars (and (not (eq? e cur-env))
  15532. (env-difference caller e cur-env ()))))
  15533. (when (pair? nvars)
  15534. (if (memq (var-name (car nvars)) '(:lambda :dilambda))
  15535. (begin
  15536. (set! env (cons (car nvars) env))
  15537. (set! nvars (cdr nvars)))
  15538. (set! vars (append nvars vars)))))
  15539. (report-usage caller head vars e))))) ; constant exprs never happen here
  15540. env)
  15541. (hash-table-set! h 'letrec letrec-walker)
  15542. (hash-table-set! h 'letrec* letrec-walker))
  15543. ;; ---------------- begin ----------------
  15544. (let ()
  15545. (define (begin-walker caller form env)
  15546. (if (not (proper-list? form))
  15547. (begin ; (begin . 1)
  15548. (lint-format "stray dot in begin? ~A" caller (truncated-list->string form))
  15549. env)
  15550. (begin
  15551. (when (pair? (cdr form))
  15552. (if (null? (cddr form)) ; (begin (f y))
  15553. (lint-format "begin could be omitted: ~A" caller (truncated-list->string form))
  15554. ;; these two are questionable -- simpler, but scope enlarged
  15555. (when (and (pair? (cadr form))
  15556. (pair? (cddr form))
  15557. (null? (cdddr form)))
  15558. (if (and (eq? (caadr form) 'do)
  15559. (< (tree-leaves (caddr form)) 24) ; or maybe (< ... (min 24 (tree-leaves do-form)))?
  15560. (not (tree-set-member (map car (cadadr form)) (caddr form))))
  15561. ;; (begin (do ((i 0 (+ i 1))) ((= i 3)) (display i)) 32) -> (do ((i 0 (+ i 1))) ((= i 3) 32) (display i))
  15562. ;; the do loop has to end normally to go on? That is, moving the following expr into the do end section is safe?
  15563. (lint-format "perhaps ~A" caller
  15564. (lists->string form
  15565. (let ((do-form (cdadr form)))
  15566. (let ((do-test (and (pair? (cadr do-form))
  15567. (caadr do-form)))
  15568. (new-end (if (and (pair? (cadr do-form))
  15569. (pair? (cdadr do-form)))
  15570. (append (cdadr do-form) (cddr form))
  15571. (cddr form))))
  15572. `(do ,(car do-form)
  15573. (,do-test ,@new-end)
  15574. ,@(cddr do-form))))))
  15575. (if (and (memq (caadr form) '(let let* letrec letrec*)) ; same for begin + let + expr -- not sure about this...
  15576. (not (symbol? (cadadr form)))
  15577. (< (tree-leaves (caddr form)) 24) ; or maybe (< ... (min 24 (tree-leaves do-form)))?
  15578. (not (tree-set-member (map car (cadadr form)) (caddr form))))
  15579. (lint-format "perhaps ~A" caller
  15580. (lists->string form
  15581. (let ((let-form (cadr form)))
  15582. `(,(car let-form) ,(cadr let-form)
  15583. ,@(if (< (tree-leaves (cddr let-form)) 60)
  15584. (cddr let-form)
  15585. (one-call-and-dots (cddr let-form)))
  15586. ,(caddr form))))))))))
  15587. (lint-walk-open-body caller 'begin (cdr form) env))))
  15588. (hash-table-set! h 'begin begin-walker))
  15589. ;; ---------------- with-baffle ----------------
  15590. (let ()
  15591. (define (with-baffle-walker caller form env)
  15592. ;; with-baffle introduces a new frame, so we need to handle it here
  15593. (lint-walk-body caller 'with-baffle (cdr form)
  15594. (cons (make-var :name :let
  15595. :initial-value form
  15596. :definer 'with-baffle)
  15597. env))
  15598. env)
  15599. (hash-table-set! h 'with-baffle with-baffle-walker))
  15600. ;; -------- with-let --------
  15601. (let ()
  15602. (define (with-let-walker caller form env)
  15603. (if (< (length form) 3)
  15604. (lint-format "~A is messed up: ~A" 'with-let caller (truncated-list->string form))
  15605. (let ((e (cadr form)))
  15606. (if (or (and (code-constant? e)
  15607. (not (let? e)))
  15608. (and (pair? e)
  15609. (let ((op (return-type (car e) env)))
  15610. (and op
  15611. (not (return-type-ok? 'let? op)))))) ; (with-let 123 123)
  15612. (lint-format "~A: first argument should be an environment: ~A" 'with-let caller (truncated-list->string form)))
  15613. (if (symbol? e)
  15614. (set-ref e caller form env)
  15615. (if (pair? e)
  15616. (begin
  15617. (if (and (null? (cdr e))
  15618. (eq? (car e) 'curlet)) ; (with-let (curlet) x)
  15619. (lint-format "~A is not needed here: ~A" 'with-let caller (truncated-list->string form)))
  15620. (lint-walk caller e (cons (make-var :name :let
  15621. :initial-value form
  15622. :definer 'with-let)
  15623. env)))))
  15624. (let ((walked #f)
  15625. (new-env (cons (make-var :name :with-let :initial-value form :definer 'with-let) env)))
  15626. (if (or (and (symbol? e)
  15627. (memq e '(*gtk* *motif* *gl* *libc* *libm* *libgdbm* *libgsl*)))
  15628. (and (pair? e)
  15629. (eq? (car e) 'sublet)
  15630. (pair? (cdr e))
  15631. (memq (cadr e) '(*gtk* *motif* *gl* *libc* *libm* *libgdbm* *libgsl*))
  15632. (set! e (cadr e))))
  15633. (let ((lib (if (defined? e)
  15634. (symbol->value e)
  15635. (let ((file (*autoload* e)))
  15636. (and (string? file)
  15637. (load file))))))
  15638. (when (let? lib)
  15639. (let-temporarily ((*e* lib))
  15640. (let ((e (lint-walk-open-body caller 'with-let (cddr form) new-env)))
  15641. (report-usage caller 'with-let
  15642. (if (eq? e env)
  15643. ()
  15644. (env-difference caller e env ()))
  15645. new-env)))
  15646. (set! walked #t))))
  15647. (unless walked
  15648. (lint-walk-open-body caller 'with-let (cddr form) new-env)))))
  15649. env)
  15650. (hash-table-set! h 'with-let with-let-walker))
  15651. ;; ---------------- defmacro ----------------
  15652. (let ()
  15653. (define (defmacro-walker caller form env)
  15654. (if (or (< (length form) 4)
  15655. (not (symbol? (cadr form))))
  15656. (begin
  15657. (lint-format "~A declaration is messed up: ~A" caller (car form) (truncated-list->string form))
  15658. env)
  15659. (let ((sym (cadr form))
  15660. (args (caddr form))
  15661. (body (cdddr form))
  15662. (head (car form)))
  15663. (if (and (pair? args)
  15664. (repeated-member? args env)) ; (defmacro hi (a b a) a)
  15665. (lint-format "~A parameter is repeated: ~A" caller head (truncated-list->string args))
  15666. (lint-format "~A is deprecated; perhaps ~A" caller head ; (defmacro hi (a b) `(+ ,a ,b))
  15667. (truncated-lists->string form
  15668. `(,(if (eq? head 'defmacro) 'define-macro 'define-macro*)
  15669. ,(cons sym args)
  15670. ,@body))))
  15671. (lint-walk-function head sym args body form env)
  15672. (cons (make-var :name sym :initial-value form :definer head) env))))
  15673. (hash-table-set! h 'defmacro defmacro-walker)
  15674. (hash-table-set! h 'defmacro* defmacro-walker))
  15675. ;; ---------------- load ----------------
  15676. (let ()
  15677. (define (load-walker caller form env)
  15678. (check-call caller 'load form env)
  15679. (if (and (pair? (cdr form))
  15680. (equal? (cadr form) ""))
  15681. (lint-format "load needs a real file name, not the empty string: ~A" caller form))
  15682. (lint-walk caller (cdr form) env)
  15683. (if (and *report-loaded-files*
  15684. (string? (cadr form)))
  15685. (catch #t
  15686. (lambda ()
  15687. (lint-file (cadr form) env))
  15688. (lambda args
  15689. env))
  15690. env))
  15691. (hash-table-set! h 'load load-walker))
  15692. ;; ---------------- require ----------------
  15693. (let ()
  15694. (define (require-walker caller form env)
  15695. (if (not (pair? (cdr form))) ; (require)
  15696. (lint-format "~A is pointless" caller form)
  15697. (if (any? string? (cdr form)) ; (require "repl.scm")
  15698. (lint-format "in s7, require's arguments should be symbols: ~A" caller (truncated-list->string form))))
  15699. (if (not *report-loaded-files*)
  15700. env
  15701. (let ((vars env))
  15702. (for-each
  15703. (lambda (f)
  15704. (let ((file (*autoload* f)))
  15705. (if (string? file)
  15706. (catch #t
  15707. (lambda ()
  15708. (set! vars (lint-file file vars)))
  15709. (lambda args
  15710. #f)))))
  15711. (cdr form))
  15712. vars)))
  15713. (hash-table-set! h 'require require-walker))
  15714. ;; ---------------- call-with-input-file etc ----------------
  15715. (let ()
  15716. (define (call-with-io-walker caller form env)
  15717. (let ((len (if (eq? (car form) 'call-with-output-string) 2 3))) ; call-with-output-string func is the first arg, not second
  15718. (when (= (length form) len)
  15719. (let ((func (list-ref form (- len 1))))
  15720. (if (= len 3)
  15721. (lint-walk caller (cadr form) env))
  15722. (if (not (and (pair? func)
  15723. (eq? (car func) 'lambda)))
  15724. (let ((f (and (symbol? func)
  15725. (symbol->value func *e*))))
  15726. (if (and (procedure? f)
  15727. (not (aritable? f 1)))
  15728. (lint-format "~A argument should be a function of one argument: ~A" caller (car form) func))
  15729. (lint-walk caller func env))
  15730. (let ((args (cadr func)))
  15731. (let ((body (cddr func))
  15732. (port (and (pair? args) (car args)))
  15733. (head (car form)))
  15734. (if (or (not port)
  15735. (pair? (cdr args)))
  15736. ;; (lambda () (write args) (newline))
  15737. (lint-format "~A argument should be a function of one argument: ~A" caller head func)
  15738. (if (and (null? (cdr body))
  15739. (pair? (car body))
  15740. (pair? (cdar body))
  15741. (eq? (cadar body) port)
  15742. (null? (cddar body)))
  15743. ;; (call-with-input-file "file" (lambda (p) (read-char p))) -> (call-with-input-file "file" read-char)
  15744. (lint-format "perhaps ~A" caller
  15745. (lists->string form
  15746. (if (= len 2)
  15747. `(,head ,(caar body))
  15748. `(,head ,(cadr form) ,(caar body)))))
  15749. (let ((cc (make-var :name port
  15750. :initial-value (list (case head
  15751. ((call-with-input-string) 'open-input-string)
  15752. ((call-with-output-string) 'open-output-string)
  15753. ((call-with-input-file) 'open-input-file)
  15754. ((call-with-output-file) 'open-output-file)))
  15755. :definer head)))
  15756. (lint-walk-body caller head body (cons cc
  15757. (cons (make-var :name :let
  15758. :initial-value form
  15759. :definer head)
  15760. env)))
  15761. (report-usage caller head (list cc) env))))))))))
  15762. env)
  15763. (for-each (lambda (op)
  15764. (hash-table-set! h op call-with-io-walker))
  15765. '(call-with-input-string call-with-input-file call-with-output-file call-with-output-string)))
  15766. ;; ---------------- catch ----------------
  15767. (let ()
  15768. (define (catch-walker caller form env)
  15769. ;; catch tag is tricky -- it is evaluated, then eq? matches at error time, so we need
  15770. ;; to catch constants that can't be eq?
  15771. (if (not (= (length form) 4))
  15772. (begin
  15773. (lint-format "catch takes 3 arguments (tag body error-handler): ~A" caller (truncated-list->string form))
  15774. (lint-walk caller (cdr form) env))
  15775. (let ((tag (cadr form)))
  15776. (if (or (and (not (pair? tag))
  15777. (or (number? tag) (char? tag) (length tag)))
  15778. (and (pair? tag)
  15779. (eq? (car tag) 'quote)
  15780. (or (not (pair? (cdr tag)))
  15781. (length (cadr tag)))))
  15782. ;; (catch #(0) (lambda () #f) (lambda a a))
  15783. (lint-format "catch tag ~S is unreliable (catch uses eq? to match tags)" caller tag))
  15784. (let ((body (caddr form))
  15785. (error-handler (cadddr form)))
  15786. ;; empty catch+catch apparently never happens
  15787. (lint-walk caller body (cons (make-var :name :let
  15788. :initial-value form
  15789. :definer 'catch)
  15790. (cons (make-var :name :catch
  15791. :initial-value form
  15792. :definer 'catch)
  15793. env)))
  15794. (lint-walk caller error-handler env))))
  15795. env)
  15796. (hash-table-set! h 'catch catch-walker))
  15797. ;; ---------------- call-with-exit etc ----------------
  15798. (let ()
  15799. (define (call-with-exit-walker caller form env)
  15800. (let ((continuation (and (pair? (cdr form))
  15801. (pair? (cadr form))
  15802. (eq? (caadr form) 'lambda)
  15803. (pair? (cdadr form))
  15804. (pair? (cddadr form))
  15805. (pair? (cadadr form))
  15806. (car (cadadr form)))))
  15807. (if (not (symbol? continuation))
  15808. (lint-walk caller (cdr form) env)
  15809. (let ((body (cddadr form))
  15810. (head (car form)))
  15811. (if (not (or (eq? head 'call-with-exit)
  15812. (eq? continuation (car body))
  15813. (tree-sym-set-member continuation '(lambda lambda* define define* curlet error apply) body)))
  15814. ;; this checks for continuation as arg (of anything), and any of set as car
  15815. ;; (call/cc (lambda (p) (+ x (p 1))))
  15816. (lint-format* caller
  15817. (string-append "perhaps " (symbol->string head))
  15818. " could be call-with-exit: "
  15819. (truncated-list->string form)))
  15820. (if (not (tree-unquoted-member continuation body))
  15821. ;; (call-with-exit (lambda (p) (+ x 1)))
  15822. (lint-format "~A ~A ~A appears to be unused: ~A" caller head
  15823. (if (eq? head 'call-with-exit) "exit function" "continuation")
  15824. continuation
  15825. (truncated-list->string form))
  15826. (let ((last (and (proper-list? body)
  15827. (list-ref body (- (length body) 1)))))
  15828. (if (and (pair? last)
  15829. (eq? (car last) continuation))
  15830. ;; (call-with-exit (lambda (return) (display x) (return (+ x y))))
  15831. (lint-format "~A is redundant here: ~A" caller continuation (truncated-list->string last)))))
  15832. (let ((cc (make-var :name continuation
  15833. :initial-value (if (eq? head 'call-with-exit) :call/exit :call/cc)
  15834. :definer head)))
  15835. (lint-walk-body caller head body (cons cc env))
  15836. (report-usage caller head (list cc) env)))))
  15837. env)
  15838. (for-each (lambda (op)
  15839. (hash-table-set! h op call-with-exit-walker))
  15840. '(call/cc call-with-current-continuation call-with-exit)))
  15841. ;; ---------------- import etc ----------------
  15842. (for-each (lambda (op)
  15843. (hash-table-set! h op (lambda (caller form env) env)))
  15844. '(define-module import export))
  15845. (hash-table-set!
  15846. h 'provide
  15847. (lambda (caller form env)
  15848. (if (not (= (length form) 2))
  15849. ;; (provide a b c)
  15850. (lint-format "provide takes one argument: ~A" caller (truncated-list->string form))
  15851. (unless (symbol? (cadr form))
  15852. (let ((op (->lint-type (cadr form))))
  15853. (if (not (memq op '(symbol? #f #t values)))
  15854. ;; (provide "test")
  15855. (lint-format "provide's argument should be a symbol: ~S" caller form)))))
  15856. env))
  15857. (hash-table-set!
  15858. h 'module ; module apparently has different syntax and expectations in various schemes
  15859. (lambda (caller form env)
  15860. (if (and (pair? (cdr form))
  15861. (pair? (cddr form)))
  15862. (lint-walk 'module (cddr form) env))
  15863. env))
  15864. (hash-table-set!
  15865. h 'define-syntax
  15866. (lambda (caller form env)
  15867. ;; we need to put the macro name in env with ftype=define-syntax
  15868. (if (and (pair? (cdr form))
  15869. (symbol? (cadr form))
  15870. (not (keyword? (cadr form)))) ; !! this thing is a disaster from the very start
  15871. (cons (make-fvar (cadr form) :ftype 'define-syntax) env)
  15872. env)))
  15873. (hash-table-set!
  15874. h 'define-method ; guile and mit-scheme have different syntaxes here
  15875. (lambda (caller form env)
  15876. (if (not (and (pair? (cdr form))
  15877. (pair? (cddr form))))
  15878. env
  15879. (if (symbol? (cadr form))
  15880. (if (keyword? (cadr form))
  15881. (lint-walk-body caller 'define-method (cdddr form) env)
  15882. (let ((new-env (if (var-member (cadr form) env)
  15883. env
  15884. (cons (make-fvar (cadr form) :ftype 'define-method) env))))
  15885. (lint-walk-body caller (cadr form) (cdddr form) new-env)))
  15886. (let ((new-env (if (var-member (caadr form) env)
  15887. env
  15888. (cons (make-fvar (caadr form) :ftype 'define-method) env))))
  15889. (lint-walk-body caller (caadr form) (cddr form) new-env))))))
  15890. (hash-table-set! h 'let-syntax (lambda (caller form env)
  15891. (lint-walk-body caller 'define-method (cddr form) env)
  15892. env))
  15893. (hash-table-set! h 'letrec-syntax (lambda (caller form env)
  15894. (lint-walk-body caller 'define-method (cddr form) env)
  15895. env))
  15896. ;; ---------------- case-lambda ----------------
  15897. (let ()
  15898. (define (case-lambda-walker caller form env)
  15899. (when (pair? (cdr form))
  15900. (let ((lens ())
  15901. (body ((if (string? (cadr form)) cddr cdr) form)) ; might have a doc string before the clauses
  15902. (doc-string (and (string? (cadr form)) (cadr form))))
  15903. (define (arg->defaults arg b1 b2 defaults)
  15904. (and defaults
  15905. (cond ((null? b1) (and (null? b2) defaults))
  15906. ((null? b2) (and (null? b1) defaults))
  15907. ((eq? arg b1) (cons b2 defaults))
  15908. ((eq? arg b2) (cons b1 defaults))
  15909. ((pair? b1)
  15910. (and (pair? b2)
  15911. (arg->defaults arg (car b1) (car b2) (arg->defaults arg (cdr b1) (cdr b2) defaults))))
  15912. (else (and (equal? b1 b2) defaults)))))
  15913. (for-each
  15914. (lambda (choice)
  15915. (if (pair? choice)
  15916. (let ((len (length (car choice))))
  15917. (if (member len lens)
  15918. ;; (case-lambda (() 0) ((x y) x) ((x y) (+ x y)) ((x y z) (+ x y z)) (args (apply + args))
  15919. (lint-format "repeated parameter list? ~A in ~A" caller (car choice) form))
  15920. (set! lens (cons len lens))
  15921. (lint-walk 'case-lambda (cons 'lambda choice) env))))
  15922. body)
  15923. (case (length lens)
  15924. ((1)
  15925. ;; (case-lambda (() (if #f #f))) -> (lambda () (if #f #f))
  15926. (lint-format "perhaps ~A" caller
  15927. (lists->string form
  15928. (if doc-string
  15929. `(let ((documentation ,doc-string))
  15930. (lambda ,(caar body) ,@(cdar body)))
  15931. `(lambda ,(caar body) ,@(cdar body))))))
  15932. ((2)
  15933. (when (let arglists-equal? ((args1 (caar body))
  15934. (args2 (caadr body)))
  15935. (if (null? args1)
  15936. (and (pair? args2) (null? (cdr args2)))
  15937. (and (pair? args1)
  15938. (if (null? args2)
  15939. (null? (cdr args1))
  15940. (and (pair? args2)
  15941. (eq? (car args1) (car args2))
  15942. (arglists-equal? (cdr args1) (cdr args2)))))))
  15943. (let* ((clause1 (car body))
  15944. (body1 (cdr clause1))
  15945. (clause2 (cadr body))
  15946. (body2 (cdr clause2))
  15947. (arglist (let ((arg1 (car clause1))
  15948. (arg2 (car clause2)))
  15949. (if (> (car lens) (cadr lens)) arg2 arg1))) ; lens is reversed
  15950. (arg-name (list-ref arglist (- (length arglist) 1)))
  15951. (diffs (arg->defaults arg-name body1 body2 ())))
  15952. (when (and (pair? diffs)
  15953. (null? (cdr diffs))
  15954. (code-constant? (car diffs)))
  15955. (let ((new-body (if (> (car lens) (cadr lens)) body2 body1))
  15956. (new-arglist (if (not (car diffs))
  15957. arglist
  15958. (if (null? (cdr arglist))
  15959. `((,arg-name ,(car diffs)))
  15960. `(,(car arglist) (,arg-name ,(car diffs)))))))
  15961. ;; (case-lambda (() (display x #f)) ((y) (display x y))) -> (lambda* (y) (display x y))
  15962. (lint-format "perhaps ~A" caller
  15963. (lists->string form
  15964. (if doc-string
  15965. `(let ((documentation ,doc-string))
  15966. (lambda* ,new-arglist ,@new-body))
  15967. `(lambda* ,new-arglist ,@new-body))))))))))))
  15968. env)
  15969. (hash-table-set! h 'case-lambda case-lambda-walker))
  15970. h))
  15971. ;; end walker-functions
  15972. ;; ----------------------------------------
  15973. (define (hash-fragment reduced-form leaves env func orig-form)
  15974. ;; func here is either #f or an env-style entry (cons name let) as produced by make-fvar,
  15975. ;; the let entries accessed are initial-value, history, arglist
  15976. (let ((old (hash-table-ref (fragments leaves) reduced-form))
  15977. (line (pair-line-number orig-form)))
  15978. ;(if func (format *stderr* "hash-fragment ~A ~A~%~%" (var-name func) reduced-form))
  15979. (if (not (vector? old))
  15980. (hash-table-set! (fragments leaves) reduced-form (vector 1 (list line) (and func (list func)) orig-form #f))
  15981. ;; key = reduced-form
  15982. ;; value = #(list uses line-numbers fvar original-form)
  15983. (begin
  15984. (vector-set! old 0 (+ (vector-ref old 0) 1))
  15985. (vector-set! old 1 (cons (pair-line-number orig-form) (vector-ref old 1)))
  15986. (when func
  15987. (if (not (vector-ref old 2))
  15988. (vector-set! old 2 (list func))
  15989. (let ((caller (if (keyword? (var-name func)) 'define (var-name func))))
  15990. (let search ((vs (vector-ref old 2)))
  15991. (when (pair? vs)
  15992. (let ((v (car vs)))
  15993. (cond ((not (eqv? (length (var-arglist v)) (length (var-arglist func))))
  15994. (search (cdr vs)))
  15995. ((eq? (var-history v) :built-in)
  15996. (lint-format "~A is the same as the built-in ~A ~A" caller
  15997. (var-name func)
  15998. (if (eq? (car (var-initial-value v)) 'define-macro) 'macro 'function)
  15999. (var-name v)))
  16000. ((not (var-member (var-name v) env))
  16001. (lint-format "~A is the same as ~A" caller
  16002. (var-name func)
  16003. (if (< 0 (pair-line-number (var-initial-value v)) 100000)
  16004. (format #f "~A (line ~D)" (var-name v) (pair-line-number (var-initial-value v)))
  16005. (if (eq? (var-name func) (var-name v))
  16006. (format #f "previous ~A" (var-name v))
  16007. (var-name v)))))
  16008. ((eq? (var-name v) (var-name func))
  16009. (lint-format "~A definition repeated: ~A" caller
  16010. (var-name func) (truncated-list->string (var-initial-value func))))
  16011. (else
  16012. (lint-format "~A could be (define ~A ~A)" caller
  16013. (var-name func) (var-name func) (var-name v)))))))
  16014. (vector-set! old 2 (cons func (vector-ref old 2))))))))))
  16015. (define (reduce-tree new-form env fvar orig-form)
  16016. ;(format *stderr* "reduce-tree: ~A ~A~%" new-form (and fvar (var-name fvar)))
  16017. (let ((leaves (tree-leaves new-form)))
  16018. (when (< 5 leaves *fragments-size*)
  16019. (call-with-exit
  16020. (lambda (quit)
  16021. (let ((outer-vars (if fvar
  16022. (do ((e (list (list (var-name fvar) (symbol "_F_") 0 ())))
  16023. (i 1 (+ i 1))
  16024. (args (args->proper-list (var-arglist fvar)) (cdr args)))
  16025. ((null? args) e)
  16026. (set! e (cons (list (car args) (symbol "_" (number->string i) "_") i ()) e)))
  16027. (list (list () '_1_) (list () '_2_) (list () '_3_))))
  16028. (local-ctr 0))
  16029. (let ((reduced-form
  16030. (let walker ((tree new-form) (vars outer-vars))
  16031. ;(format *stderr* "walker: ~A, vars: ~A~%" tree vars)
  16032. (cond ((or (not (symbol? tree))
  16033. (keyword? tree))
  16034. (if (or (not (pair? tree))
  16035. (eq? (car tree) 'quote))
  16036. tree
  16037. (case (car tree)
  16038. ((let let*)
  16039. ;; in let we need to sort locals by order of appearance in the body
  16040. (if (not (and (pair? (cdr tree))
  16041. (pair? (cddr tree))))
  16042. (quit))
  16043. (let ((locals ())
  16044. (body ())
  16045. (named-let (symbol? (cadr tree)))
  16046. (lvars ()))
  16047. (if named-let
  16048. (begin
  16049. (set! lvars (cons (list (cadr tree) (symbol "_NL" (number->string local-ctr) "_") -1) lvars))
  16050. (set! local-ctr (+ local-ctr 1))
  16051. (set! locals (caddr tree))
  16052. (set! body (cdddr tree)))
  16053. (begin
  16054. (set! locals (cadr tree))
  16055. (set! body (cddr tree))))
  16056. (if (not (list? locals)) (quit))
  16057. (if (eq? (car tree) 'let)
  16058. (for-each (lambda (local)
  16059. (if (not (and (pair? local) (pair? (cdr local)))) (quit))
  16060. (set! lvars (cons (list (car local) () 0 (walker (cadr local) vars)) lvars)))
  16061. locals)
  16062. (for-each (lambda (local)
  16063. (if (not (and (pair? local) (pair? (cdr local)))) (quit))
  16064. (set! lvars (cons (list (car local)
  16065. (symbol "_L" (number->string local-ctr) "_")
  16066. local-ctr
  16067. (walker (cadr local) (append lvars vars)))
  16068. lvars))
  16069. (set! local-ctr (+ local-ctr 1)))
  16070. locals))
  16071. ;; now walk the body, setting the reduced local name by order of encounter (in let, not let*)
  16072. (let ((new-body (walker body (append lvars vars))))
  16073. (when (and (eq? (car tree) 'let)
  16074. ;; fill-in unused-var dummy names etc
  16075. (pair? lvars))
  16076. (for-each (lambda (v)
  16077. (when (null? (cadr v))
  16078. (list-set! v 1 (symbol "_L" (number->string local-ctr) "_"))
  16079. (list-set! v 2 local-ctr)
  16080. (set! local-ctr (+ local-ctr 1))))
  16081. lvars))
  16082. (set! lvars (sort! lvars (lambda (a b) (< (caddr a) (caddr b)))))
  16083. (if named-let
  16084. `(,(car tree) ,(cadr (assq (cadr tree) lvars))
  16085. ,(map (lambda (v) (list (cadr v) (cadddr v))) (cdr lvars))
  16086. ,@new-body)
  16087. `(,(car tree) ,(map (lambda (v) (list (cadr v) (cadddr v))) lvars)
  16088. ,@new-body)))))
  16089. ((letrec letrec*)
  16090. (if (not (pair? (cdr tree))) (quit))
  16091. (let ((locals (cadr tree))
  16092. (body (cddr tree))
  16093. (lvars ()))
  16094. (if (not (and (list? locals) (pair? body))) (quit))
  16095. (for-each (lambda (local)
  16096. (if (not (and (pair? local)
  16097. (pair? (cdr local))))
  16098. (quit))
  16099. (set! lvars (cons (list (car local)
  16100. (symbol "_L" (number->string local-ctr) "_")
  16101. local-ctr ())
  16102. lvars))
  16103. (set! local-ctr (+ local-ctr 1)))
  16104. locals)
  16105. (for-each (lambda (local lv)
  16106. (list-set! lv 3 (walker (cadr local) lvars)))
  16107. locals lvars)
  16108. `(,(car tree)
  16109. ,(map (lambda (v) (list (cadr v) (cadddr v))) lvars)
  16110. ,@(walker body (append lvars vars)))))
  16111. ((do)
  16112. (if (not (and (pair? (cdr tree))
  16113. (list? (cadr tree))
  16114. (pair? (cddr tree))
  16115. (list? (cdddr tree))))
  16116. (quit))
  16117. (let ((locals (cadr tree))
  16118. (end+result (caddr tree))
  16119. (body (cdddr tree))
  16120. (lvars ()))
  16121. (if (not (list? end+result)) (quit))
  16122. (for-each (lambda (local)
  16123. (if (not (and (pair? local)
  16124. (pair? (cdr local))))
  16125. (quit))
  16126. (set! lvars (cons (list (car local)
  16127. () 0
  16128. (walker (cadr local) vars)
  16129. (if (pair? (cddr local))
  16130. (caddr local)
  16131. :unset))
  16132. lvars)))
  16133. locals)
  16134. (let ((new-env (append lvars vars)))
  16135. (let ((new-end (walker end+result new-env))
  16136. (new-body (walker body new-env)))
  16137. (when (pair? lvars)
  16138. (for-each (lambda (lv)
  16139. (if (not (eq? (lv 4) :unset))
  16140. (list-set! lv 4 (walker (lv 4) new-env))))
  16141. lvars)
  16142. (for-each (lambda (v)
  16143. (when (null? (cadr v))
  16144. (list-set! v 1 (symbol "_L" (number->string local-ctr) "_"))
  16145. (list-set! v 2 local-ctr)
  16146. (set! local-ctr (+ local-ctr 1))))
  16147. lvars)
  16148. (set! lvars (sort! lvars (lambda (a b) (< (caddr a) (caddr b))))))
  16149. `(do ,(map (lambda (v)
  16150. (if (eq? (v 4) :unset)
  16151. (list (v 1) (v 3))
  16152. (list (v 1) (v 3) (v 4))))
  16153. lvars)
  16154. ,new-end
  16155. ,@new-body)))))
  16156. ((lambda)
  16157. (if (not (and (pair? (cdr tree))
  16158. (proper-list? (cddr tree))))
  16159. (quit))
  16160. (let* ((lvars (map (lambda (a)
  16161. (let ((res (list a (symbol "_A" (number->string local-ctr) "_") local-ctr)))
  16162. (set! local-ctr (+ local-ctr 1))
  16163. res))
  16164. (let ((args (args->proper-list (cadr tree))))
  16165. (if (pair? args) args (quit)))))
  16166. (new-body (let ((new-vars (append lvars vars)))
  16167. (map (lambda (p) (walker p new-vars)) (cddr tree))))
  16168. (new-args (if (symbol? (cadr tree))
  16169. (cadar lvars)
  16170. (if (proper-list? (cadr tree))
  16171. (map cadr lvars)
  16172. (let ((lst (map cadr lvars)))
  16173. (append (copy lst (make-list (- (length lst) 1)))
  16174. (list-ref lst (- (length lst) 1))))))))
  16175. `(lambda ,new-args ,@new-body)))
  16176. ((lambda*)
  16177. (if (not (and (pair? (cdr tree))
  16178. (or (symbol? (cadr tree))
  16179. (proper-list? (cadr tree)))))
  16180. (quit))
  16181. (let* ((lvars (map (lambda (a)
  16182. (if (memq a '(:rest :allow-other-keys))
  16183. (values)
  16184. (let ((res (list (if (pair? a) (car a) a)
  16185. (symbol "_A" (number->string local-ctr) "_") local-ctr)))
  16186. (set! local-ctr (+ local-ctr 1))
  16187. res)))
  16188. (args->proper-list (cadr tree))))
  16189. (new-body (let ((new-vars (append lvars vars)))
  16190. (map (lambda (p) (walker p new-vars)) (cddr tree))))
  16191. (new-args (if (symbol? (cadr tree))
  16192. (cadar lvars)
  16193. (map (lambda (a)
  16194. (cond ((keyword? a) a)
  16195. ((symbol? a) (cadr (assq a lvars)))
  16196. ((and (pair? a)
  16197. (pair? (cdr a)))
  16198. (list (assq a lvars) (cadr a)))
  16199. (else (quit))))
  16200. (cadr tree)))))
  16201. `(lambda* ,new-args ,@new-body)))
  16202. ((case)
  16203. (if (not (and (pair? (cdr tree))
  16204. (pair? (cddr tree))
  16205. (pair? (caddr tree))))
  16206. (quit))
  16207. `(case ,(walker (cadr tree) vars)
  16208. ,(map (lambda (c)
  16209. (if (not (and (pair? c)
  16210. (pair? (cdr c))))
  16211. (quit))
  16212. (cons (car c)
  16213. (map (lambda (p) (walker p vars)) (cdr c))))
  16214. (cddr tree))))
  16215. ((if)
  16216. (if (not (and (pair? (cdr tree))
  16217. (pair? (cddr tree))
  16218. (list? (cdddr tree))))
  16219. (quit))
  16220. (let ((expr (walker (cadr tree) vars))
  16221. (true (walker (caddr tree) vars)))
  16222. (if (null? (cdddr tree))
  16223. (if (and (pair? expr)
  16224. (eq? (car expr) 'not))
  16225. `(unless ,(cadr expr) ,@(unbegin true))
  16226. `(when ,expr ,@(unbegin true)))
  16227. `(if ,expr ,true ,(walker (cadddr tree) vars)))))
  16228. ((when unless)
  16229. (if (not (and (pair? (cdr tree))
  16230. (pair? (cddr tree))))
  16231. (quit))
  16232. `(,(car tree) ,(walker (cadr tree) vars)
  16233. ,@(map (lambda (p) (walker p vars)) (cddr tree))))
  16234. ((set!)
  16235. (if (not (and (pair? (cdr tree)) (pair? (cddr tree)))) (quit))
  16236. (if (symbol? (cadr tree))
  16237. (let ((v (assq (cadr tree) vars)))
  16238. (if (or (not v) ; if not a var, it's about to be an outer-var
  16239. (and (not fvar)
  16240. (memq (cadr v) '(_1_ _2_ _3_))))
  16241. (quit))
  16242. (when (null? (cadr v)) ; must be a previously unencountered local
  16243. (list-set! v 1 (symbol "_L" (number->string local-ctr) "_"))
  16244. (list-set! v 2 local-ctr)
  16245. (set! local-ctr (+ local-ctr 1)))
  16246. `(set! ,(cadr v) ,(walker (caddr tree) vars)))
  16247. `(set! ,(walker (cadr tree) vars) ,(walker (caddr tree) vars))))
  16248. ((define define*
  16249. ;; these propagate backwards and we're not returning the new env in this loop,
  16250. ;; lvars can be null, so splicing a new local into vars is a mess,
  16251. ;; but if the defined name is not reduced, it can occur later as itself (not via car),
  16252. ;; so without lots of effort (a dummy var if null lvars, etc), we can only handle
  16253. ;; functions within a function (fvar not #f).
  16254. ;; but adding that possibility got no hits
  16255. define-constant define-macro define-macro*
  16256. define-syntax let-syntax letrec-syntax match syntax-rules case-lambda
  16257. require import module cond-expand quasiquote reader-cond while unquote
  16258. call-with-values let-values define-values let*-values multiple-value-bind)
  16259. (quit))
  16260. (else
  16261. (cons (cond ((pair? (car tree))
  16262. (walker (car tree) vars))
  16263. ((assq (car tree) vars) =>
  16264. (lambda (v) (if (symbol? (cadr v)) (cadr v) (car tree))))
  16265. (else (car tree)))
  16266. (if (pair? (cdr tree))
  16267. (map (lambda (p)
  16268. (walker p vars))
  16269. (cdr tree))
  16270. (cdr tree)))))))
  16271. ((assq tree vars) => ; replace in-tree symbol with its reduction
  16272. (lambda (v)
  16273. ;; v is a list: local-name possible-reduced-name [counter value]
  16274. (when (null? (cadr v))
  16275. (list-set! v 1 (symbol "_L" (number->string local-ctr) "_"))
  16276. (list-set! v 2 local-ctr)
  16277. (set! local-ctr (+ local-ctr 1)))
  16278. (cadr v)))
  16279. (else
  16280. (if fvar (quit))
  16281. (let set-outer ((ovars outer-vars))
  16282. (if (null? ovars)
  16283. (quit)
  16284. (if (null? (caar ovars))
  16285. (begin
  16286. (set-car! (car ovars) tree)
  16287. (cadar ovars))
  16288. (set-outer (cdr ovars))))))))))
  16289. ;; if->when, for example, so tree length might change
  16290. (set! leaves (tree-leaves reduced-form))
  16291. (hash-fragment reduced-form leaves env fvar orig-form)
  16292. (if (and (memq (car reduced-form) '(or and))
  16293. (> (length reduced-form) 3))
  16294. (do ((i (- (length reduced-form) 1) (- i 1))
  16295. (rfsize leaves))
  16296. ((or (= i 2)
  16297. (< rfsize 6)))
  16298. (let ((rf (copy reduced-form (make-list i))))
  16299. (set! rfsize (tree-leaves rf))
  16300. (when (> rfsize 5)
  16301. (hash-fragment rf rfsize env #f orig-form)))))
  16302. (when fvar (quit))
  16303. ;; TODO: also below and clean this up!
  16304. (unless (and (pair? lint-function-body)
  16305. (equal? new-form (car lint-function-body)))
  16306. (let ((fvars (let ((fcase (hash-table-ref (fragments leaves) (list reduced-form))))
  16307. (and (vector? fcase)
  16308. (vector-ref fcase 2)))))
  16309. (when (pair? fvars)
  16310. (call-with-exit
  16311. (lambda (ok)
  16312. (for-each (lambda (fv)
  16313. (when (var-member (var-name fv) env)
  16314. (format outport "~NCperhaps ~A -> (~A~{ ~A~})~%" lint-left-margin #\space
  16315. (truncated-list->string new-form)
  16316. (var-name fv)
  16317. (map (lambda (a)
  16318. (if (null? (car a))
  16319. (values)
  16320. (car a)))
  16321. outer-vars))
  16322. (ok)))
  16323. fvars)
  16324. (format outport "~NCif '~A were in scope, perhaps ~A -> (~A~{ ~A~})~%" lint-left-margin #\space
  16325. (var-name (car fvars))
  16326. (truncated-list->string new-form)
  16327. (var-name (car fvars))
  16328. (map (lambda (a)
  16329. (if (null? (car a))
  16330. (values)
  16331. (car a)))
  16332. outer-vars)))))))
  16333. ;; now look for (f _1_) -> _1_ possibilities
  16334. ;; every reference to _1_ has to be via (f _1_), and f must have no side-effects
  16335. ;; so first rescan the form, gathering info about each _n_ var
  16336. (let* ((rnames (map (lambda (v)
  16337. (if (symbol? (car v))
  16338. (cadr v)
  16339. (values)))
  16340. outer-vars))
  16341. (rvars (map (lambda (v)
  16342. (vector v 0 ()))
  16343. rnames)))
  16344. (when (and (pair? reduced-form)
  16345. (not (eq? (car reduced-form) 'quote)))
  16346. (let walker ((tree reduced-form))
  16347. (for-each (lambda (p)
  16348. (if (pair? p)
  16349. (if (not (eq? (car p) 'quote))
  16350. (walker p))
  16351. (if (and (symbol? p)
  16352. (memq p rnames))
  16353. (let search ((rv rvars))
  16354. (let ((v (car rv)))
  16355. (if (eq? (v 0) p)
  16356. (begin
  16357. (set! (v 1) (+ (v 1) 1))
  16358. (set! (v 2) (cons tree (v 2))))
  16359. (search (cdr rv))))))))
  16360. tree)))
  16361. (let ((reducibles ()))
  16362. (for-each (lambda (v)
  16363. (if (and (pair? (v 2))
  16364. (pair? (car (v 2)))
  16365. (pair? (cdar (v 2)))
  16366. (null? (cddar (v 2)))
  16367. (not (side-effect-with-vars? (car (v 2)) env rnames))
  16368. (or (= (v 1) 1)
  16369. (let ((first (car (v 2))))
  16370. (not (member first (cdr (v 2))
  16371. (lambda (a b)
  16372. (not (equal? a b))))))))
  16373. (set! reducibles (cons (car (v 2)) reducibles))))
  16374. rvars)
  16375. ;; reducibles is a list of _n_ vars that can be simplified one more level
  16376. (when (pair? reducibles)
  16377. (for-each (lambda (r)
  16378. (let ((rf (let walker ((tree reduced-form))
  16379. (if (or (not (pair? tree))
  16380. (eq? (car tree) 'quote))
  16381. tree
  16382. (if (equal? tree r)
  16383. (cadr tree)
  16384. (cons (walker (car tree))
  16385. (walker (cdr tree))))))))
  16386. (set! leaves (tree-leaves rf))
  16387. (when (> leaves 5)
  16388. (hash-fragment rf leaves env fvar orig-form))))
  16389. reducibles)
  16390. ;; if more than one reducible, try all combinations
  16391. (when (pair? (cdr reducibles))
  16392. (let ((combo (if (null? (cddr reducibles))
  16393. (list (list (reducibles 0) (reducibles 1)))
  16394. (list (list (reducibles 0) (reducibles 1))
  16395. (list (reducibles 0) (reducibles 2))
  16396. (list (reducibles 1) (reducibles 2))
  16397. (list (reducibles 0) (reducibles 1) (reducibles 2))))))
  16398. (for-each (lambda (r)
  16399. (let ((rf (let walker ((tree reduced-form))
  16400. (if (or (not (pair? tree))
  16401. (eq? (car tree) 'quote))
  16402. tree
  16403. (if (member tree r)
  16404. (cadr tree)
  16405. (cons (walker (car tree))
  16406. (walker (cdr tree))))))))
  16407. (set! leaves (tree-leaves rf))
  16408. (when (> (tree-leaves rf) 5)
  16409. (hash-fragment rf leaves env fvar orig-form))))
  16410. combo)))))))))))))
  16411. (define (lint-fragment form env)
  16412. (if (memq (car form) '(or and))
  16413. ;; or/and are special because leading and trailing cases are separable (like leading cases for bodies)
  16414. (do ((i (length form) (- i 1))
  16415. (p (cdr form) (cdr p)))
  16416. ((<= i 2))
  16417. (reduce-tree (cons (car form) p) env #f form))
  16418. (reduce-tree form env #f form)))
  16419. (define (reduce-function-tree fvar env)
  16420. (let ((definition (cond ((var-initial-value fvar) => cddr) (else #f))))
  16421. (when (pair? definition)
  16422. (reduce-tree (if (and (string? (car definition))
  16423. (pair? (cdr definition)))
  16424. (cdr definition)
  16425. definition)
  16426. env
  16427. (and (not (keyword? (var-name fvar)))
  16428. fvar)
  16429. (var-initial-value fvar)))))
  16430. ;; ----------------------------------------
  16431. (define lint-walk-pair
  16432. (let ((unsafe-makers '(sublet inlet copy cons list append make-shared-vector vector hash-table hash-table*
  16433. make-hash-table make-hook #_{list} #_{append} gentemp or and not))
  16434. (qq-form #f))
  16435. (lambda (caller form env)
  16436. (let ((head (car form)))
  16437. (set! line-number (pair-line-number form))
  16438. (lint-fragment form env)
  16439. (cond
  16440. ((hash-table-ref walker-functions head)
  16441. => (lambda (f)
  16442. (f caller form env)))
  16443. (else
  16444. (if (not (proper-list? form))
  16445. ;; these appear to be primarily macro/match arguments
  16446. ;; other cases (not list) have already been dealt with far above
  16447. (if (and (pair? form)
  16448. (symbol? head)
  16449. (procedure? (symbol->value head *e*)))
  16450. ;; (+ . 1)
  16451. (lint-format "unexpected dot: ~A" caller (truncated-list->string form)))
  16452. (begin
  16453. (cond ((symbol? head)
  16454. (let ((v (var-member head env)))
  16455. (if (and (var? v)
  16456. (not (memq form (var-history v))))
  16457. (set! (var-history v) (cons form (var-history v))))
  16458. (check-call caller head form env)
  16459. ;; look for one huge argument leaving lonely trailing arguments somewhere off the screen
  16460. ;; (it needs to be one arg, not a call on values)
  16461. (let ((branches (length form)))
  16462. (when (and (= branches 2)
  16463. (any-procedure? head env)
  16464. (not (eq? head 'unquote)))
  16465. (let ((arg (cadr form)))
  16466. ;; begin=(car arg) happens very rarely
  16467. (when (pair? arg)
  16468. (when (and (memq (car arg) '(let let*))
  16469. (not (or (symbol? (cadr arg))
  16470. (and (pair? (cddr arg))
  16471. (pair? (caddr arg))
  16472. (eq? 'lambda (caaddr arg)))
  16473. (assq head (cadr arg)))))
  16474. ;; (string->symbol (let ((s (copy vstr))) (set! (s (+ pos 1)) #\s) s)) ->
  16475. ;; (let ((s (copy vstr))) (set! (s (+ pos 1)) #\s) (string->symbol s))")
  16476. (lint-format "perhaps~%~NC~A ->~%~NC~A" caller
  16477. (+ lint-left-margin 4) #\space
  16478. (truncated-list->string form)
  16479. (+ lint-left-margin 4) #\space
  16480. (let* ((body (cddr arg))
  16481. (len (- (length body) 1))
  16482. (str (object->string `(,(car arg) ,(cadr arg)
  16483. ,@(copy body (make-list len))
  16484. (,head ,(list-ref body len))))))
  16485. (if (<= (length str) target-line-length)
  16486. str
  16487. (format #f "(~A ... (~A ~A))"
  16488. (car arg) head
  16489. (truncated-list->string (list-ref body len)))))))
  16490. (when (eq? (car arg) 'or)
  16491. (let ((else-clause (let ((last-clause (list-ref arg (- (length arg) 1))))
  16492. (if (and (pair? last-clause)
  16493. (memq (car last-clause) '(error throw)))
  16494. last-clause
  16495. (if (or (not (code-constant? last-clause))
  16496. (side-effect? `(,head ,last-clause) env))
  16497. :checked-eval-error
  16498. (let ((res (checked-eval `(,head ,last-clause))))
  16499. (if (or (and (symbol? res)
  16500. (not (eq? res :checked-eval-error)))
  16501. (pair? res))
  16502. (list 'quote res)
  16503. res)))))))
  16504. (unless (eq? else-clause :checked-eval-error)
  16505. (set! last-rewritten-internal-define form)
  16506. ;; (string->number (or (f x) "4")) -> (cond ((f x) => string->number) (else 4))
  16507. (lint-format "perhaps ~A" caller
  16508. (lists->string form
  16509. `(cond (,(if (or (null? (cddr arg))
  16510. (null? (cdddr arg)))
  16511. (cadr arg)
  16512. (copy arg (make-list (- (length arg) 1))))
  16513. => ,head)
  16514. (else ,else-clause))))))))))
  16515. (unless (or (<= branches 2)
  16516. (any-macro? head env)
  16517. (memq head '(for-each map #_{list} * + - /)))
  16518. (let ((leaves (tree-leaves form)))
  16519. (when (> leaves (max *report-bloated-arg* (* branches 3)))
  16520. (do ((p (cdr form) (cdr p))
  16521. (i 1 (+ i 1)))
  16522. ((or (not (pair? p))
  16523. (null? (cdr p))
  16524. (and (pair? (car p))
  16525. (symbol? (caar p))
  16526. (not (memq (caar p) '(lambda quote call/cc list vector match-lambda values)))
  16527. (> (tree-leaves (car p)) (- leaves (* branches 2)))
  16528. (or (not (memq head '(or and)))
  16529. (= i 1))
  16530. (not (tree-member 'values (car p)))
  16531. (let ((header (copy form (make-list i)))
  16532. (trailer (copy form (make-list (- branches i 1)) (+ i 1)))
  16533. (disclaimer (if (or (any-procedure? head env)
  16534. (hash-table-ref no-side-effect-functions head))
  16535. ""
  16536. (format #f ", assuming ~A is not a macro," head))))
  16537. ;; begin=(caar p) here is almost entirely as macro arg
  16538. ;; (apply env-channel (make-env ...) args) -> (let ((_1_ (make-env ...))) (apply env-channel _1_ args))
  16539. (lint-format "perhaps~A~%~NC~A ->~%~NC~A" caller
  16540. disclaimer
  16541. (+ lint-left-margin 4) #\space
  16542. (lint-pp `(,@header ,(one-call-and-dots (car p)) ,@trailer))
  16543. (+ lint-left-margin 4) #\space
  16544. (if (and (memq (caar p) '(let let*))
  16545. (list? (cadar p))
  16546. (not (assq head (cadar p)))) ; actually not intersection header+trailer (map car cadr)
  16547. (let ((last (let ((body (cddar p)))
  16548. (list-ref body (- (length body) 1)))))
  16549. (if (< (tree-leaves last) 12)
  16550. (format #f "(~A ... ~A)"
  16551. (caar p)
  16552. (lint-pp `(,@header ,last ,@trailer)))
  16553. (lint-pp `(let ((_1_ ,(one-call-and-dots (car p))))
  16554. (,@header _1_ ,@trailer)))))
  16555. (lint-pp `(let ((_1_ ,(one-call-and-dots (car p))))
  16556. (,@header _1_ ,@trailer)))))
  16557. #t)))))))))
  16558. (when (pair? form)
  16559. ;; save any references to vars in their var-history (type checked later)
  16560. ;; this can be fooled by macros, as everywhere else
  16561. (for-each (lambda (arg)
  16562. (if (symbol? arg)
  16563. (let ((v (var-member arg env)))
  16564. (if (and (var? v)
  16565. (not (memq form (var-history v))))
  16566. (set! (var-history v) (cons form (var-history v)))))))
  16567. form)
  16568. (if (set!? form env)
  16569. (set-set (cadr form) caller form env)))
  16570. (if (var? v)
  16571. (if (and (memq (var-ftype v) '(define lambda define* lambda*))
  16572. (not (memq caller (var-scope v))))
  16573. (let ((cv (var-member caller env)))
  16574. (set! (var-scope v)
  16575. (cons (if (and (var? cv)
  16576. (memq (var-ftype cv) '(define lambda define* lambda*))) ; named-let does not define ftype
  16577. caller
  16578. (cons caller env))
  16579. (var-scope v)))))
  16580. (begin
  16581. (cond ((hash-table-ref special-case-functions head)
  16582. => (lambda (f)
  16583. (f caller head form env))))
  16584. ;; change (list ...) to '(....) if it's safe as a constant list
  16585. ;; and (vector ...) -> #(...)
  16586. (if (and (pair? (cdr form))
  16587. (hash-table-ref no-side-effect-functions head)
  16588. (not (memq head unsafe-makers)))
  16589. (for-each (lambda (p)
  16590. (if (let constable? ((cp p))
  16591. (and (pair? cp)
  16592. (memq (car cp) '(list vector))
  16593. (pair? (cdr cp))
  16594. (every? (lambda (inp)
  16595. (or (code-constant? inp)
  16596. (constable? inp)))
  16597. (cdr cp))))
  16598. (lint-format "perhaps ~A -> ~A~A" caller
  16599. (truncated-list->string p)
  16600. (if (eq? (car p) 'list) "'" "")
  16601. (object->string (eval p)))))
  16602. (cdr form)))
  16603. (if (and (not (= line-number last-simplify-numeric-line-number))
  16604. (hash-table-ref numeric-ops head)
  16605. (proper-tree? form))
  16606. (let ((val (simplify-numerics form env)))
  16607. (if (not (equal-ignoring-constants? form val))
  16608. (begin
  16609. (set! last-simplify-numeric-line-number line-number)
  16610. ;; (+ 1 2) -> 3, and many others
  16611. (lint-format "perhaps ~A" caller (lists->string form val))))))
  16612. ;; if a var is used before it is defined, the var history and ref/set
  16613. ;; info needs to be saved until the definition, so other-identifiers collects it
  16614. (unless (defined? head (rootlet))
  16615. (hash-table-set! other-identifiers head
  16616. (if (not (hash-table-ref other-identifiers head))
  16617. (list form)
  16618. (cons form (hash-table-ref other-identifiers head)))))))
  16619. ;; ----------------
  16620. ;; (f ... (if A B C) (if A D E) ...) -> (f ... (if A (values B D) (values C E)) ...)
  16621. ;; these happen up to almost any number of clauses
  16622. ;; need true+false in every case, and need to be contiguous
  16623. ;; case/cond happen here, but very rarely in a way we can combine via values
  16624. (unless (any-macro? head env) ; actually most macros are safe here...
  16625. (let ((p (member 'if (cdr form) (lambda (x q)
  16626. (and (pair? q)
  16627. (eq? (car q) 'if) ; it's an if expression
  16628. (pair? (cdr q))
  16629. (pair? (cddr q)) ; there's a true branch
  16630. (pair? (cdddr q))))))) ; and a false branch (similarly below)
  16631. (when (pair? p)
  16632. (do ((test (cadar p))
  16633. (q (cdr p) (cdr q)))
  16634. ((not (and (pair? q)
  16635. (let ((x (car q)))
  16636. (and (pair? x)
  16637. (eq? (car x) 'if)
  16638. (pair? (cdr x))
  16639. (equal? (cadr x) test)
  16640. (pair? (cddr x))
  16641. (pair? (cdddr x))))))
  16642. (unless (eq? q (cdr p))
  16643. (let ((header (do ((i 1 (+ i 1))
  16644. (r (cdr form) (cdr r)))
  16645. ((eq? r p)
  16646. (copy form (make-list i)))))
  16647. (middle (do ((r p (cdr r))
  16648. (trues ())
  16649. (falses ()))
  16650. ((eq? r q)
  16651. `(if ,test
  16652. (values ,@(reverse trues))
  16653. (values ,@(reverse falses))))
  16654. (set! trues (cons (caddar r) trues))
  16655. (set! falses (cons (car (cdddar r)) falses)))))
  16656. ;; (+ (if A B C) (if A C D) y) -> (+ (if A (values B C) (values C D)) y)
  16657. (lint-format "perhaps~A ~A" caller
  16658. (if (side-effect? test env)
  16659. (format #f " (ignoring ~S's possible side-effects)" test)
  16660. "")
  16661. (lists->string form `(,@header ,middle ,@q))))))))))))
  16662. ((pair? head)
  16663. (cond ((not (and (pair? (cdr head))
  16664. (memq (car head) '(lambda lambda*)))))
  16665. ((and (identity? head)
  16666. (pair? (cdr form))) ; identity needs an argument
  16667. ;; ((lambda (x) x) 32) -> 32
  16668. (lint-format "perhaps ~A" caller (truncated-lists->string form (cadr form))))
  16669. ((and (symbol? (cadr head)) ; ((lambda x x) 1 2 3) -> (list 1 2 3)
  16670. (pair? (cddr head))
  16671. (eq? (cadr head) (caddr head))
  16672. (null? (cdddr head)))
  16673. (lint-format "perhaps ~A" caller
  16674. (lists->string form
  16675. `(list ,@(cdr form)))))
  16676. ((and (null? (cadr head))
  16677. (pair? (cddr head)))
  16678. ;; ((lambda () 32) 0) -> 32
  16679. (lint-format "perhaps ~A" caller
  16680. (truncated-lists->string
  16681. form
  16682. (if (and (null? (cdddr head))
  16683. (not (and (pair? (caddr head))
  16684. (memq (caaddr head) '(define define* define-constant define-macro define-macro*)))))
  16685. (caddr head)
  16686. `(let () ,@(cddr head))))))
  16687. ((and (pair? (cddr head)) ; ((lambda (...) ...) ...) -> (let ...) -- lambda here is ugly and slow
  16688. (proper-list? (cddr head))
  16689. (not (any? (lambda (a) (mv-range a env)) (cdr form))))
  16690. (call-with-exit
  16691. (lambda (quit) ; uncountably many things can go wrong with the lambda form
  16692. (let ((vars ())
  16693. (vals ()))
  16694. (do ((v (cadr head) (cdr v))
  16695. (a (cdr form) (cdr a)))
  16696. ((not (and (pair? a)
  16697. (pair? v)))
  16698. (if (symbol? v)
  16699. (begin
  16700. (set! vars (cons v vars))
  16701. (set! vals (cons `(list ,@a) vals)))
  16702. (do ((v v (cdr v)))
  16703. ((not (pair? v)))
  16704. (if (not (pair? v))
  16705. (quit))
  16706. (if (pair? (car v))
  16707. (begin
  16708. (if (not (pair? (cdar v)))
  16709. (quit))
  16710. (set! vars (cons (caar v) vars))
  16711. (set! vals (cons (cadar v) vals)))
  16712. (begin
  16713. (set! vars (cons (car v) vars))
  16714. (set! vals (cons #f vals)))))))
  16715. (set! vars (cons ((if (pair? (car v)) caar car) v) vars))
  16716. (set! vals (cons (car a) vals)))
  16717. ;; ((lambda* (a b) (+ a b)) 1) -> (let ((a 1) (b #f)) (+ a b))
  16718. (lint-format "perhaps ~A" caller
  16719. (lists->string form
  16720. `(,(if (or (eq? (car head) 'lambda)
  16721. (not (pair? (cadr head)))
  16722. (null? (cdadr head)))
  16723. 'let 'let*)
  16724. ,(map list (reverse vars) (reverse vals))
  16725. ,@(cddr head))))))))))
  16726. ((and (procedure? head)
  16727. (memq head '(#_{list} #_{apply_values} #_{append})))
  16728. (for-each (lambda (p)
  16729. (let ((sym (and (symbol? p) p)))
  16730. (when sym
  16731. (let ((v (var-member sym env)))
  16732. (if (var? v)
  16733. (set-ref sym caller form env)
  16734. (if (not (defined? sym (rootlet)))
  16735. (hash-table-set! other-identifiers sym
  16736. (if (not (hash-table-ref other-identifiers sym))
  16737. (list form)
  16738. (cons form (hash-table-ref other-identifiers sym))))))))))
  16739. (cdr form))
  16740. (when (and (eq? head #_{list})
  16741. (not (eq? lint-current-form qq-form)))
  16742. (set! qq-form lint-current-form) ; only interested in simplest cases here
  16743. (case (length form)
  16744. ((2)
  16745. (if (and (pair? (cadr form))
  16746. (eq? (caadr form) #_{apply_values}) ; `(,@x) -> (copy x)
  16747. (not (qq-tree? (cadadr form))))
  16748. (lint-format "perhaps ~A" caller
  16749. (lists->string form
  16750. (un_{list} (if (pair? (cadadr form))
  16751. (cadadr form)
  16752. `(copy ,(cadadr form))))))
  16753. (if (symbol? (cadr form))
  16754. (lint-format "perhaps ~A" caller ; `(,x) -> (list x)
  16755. (lists->string form `(list ,(cadr form)))))))
  16756. ((3)
  16757. (if (and (pair? (caddr form))
  16758. (eq? (caaddr form) #_{apply_values})
  16759. (not (qq-tree? (cadr (caddr form))))
  16760. (pair? (cadr form)) ; `(,@x ,@y) -> (append x y)
  16761. (eq? (caadr form) #_{apply_values})
  16762. (not (qq-tree? (cadadr form))))
  16763. (lint-format "perhaps ~A" caller
  16764. (lists->string form
  16765. `(append ,(un_{list} (cadadr form))
  16766. ,(un_{list} (cadr (caddr form))))))))
  16767. (else
  16768. (if (every? (lambda (a) ; `(,@x ,@y etc) -> (append x y ...)
  16769. (and (pair? a)
  16770. (eq? (car a) #_{apply_values})
  16771. (not (qq-tree? (cdr a)))))
  16772. (cdr form))
  16773. (lint-format "perhaps ~A" caller
  16774. (lists->string form `(append ,@(map (lambda (a)
  16775. (un_{list} (cadr a)))
  16776. (cdr form)))))))
  16777. ))))
  16778. (let ((vars env))
  16779. (for-each
  16780. (lambda (f)
  16781. (set! vars (lint-walk caller f vars)))
  16782. form))))
  16783. env))))))
  16784. (define (lint-walk caller form env)
  16785. (cond ((symbol? form)
  16786. (if (memq form '(+i -i))
  16787. (format outport "~NC~A is not a number in s7~%" lint-left-margin #\space form))
  16788. (set-ref form caller #f env)) ; returns env
  16789. ((pair? form)
  16790. (lint-walk-pair caller form env))
  16791. ((string? form)
  16792. (let ((len (length form)))
  16793. (if (and (> len 16)
  16794. (string=? form (make-string len (string-ref form 0))))
  16795. ;; "*****************************" -> (format #f "~NC" 29 #\*)
  16796. (lint-format "perhaps ~S -> ~A" caller form `(format #f "~NC" ,len ,(string-ref form 0)))))
  16797. env)
  16798. ((vector? form)
  16799. (let ((happy #t))
  16800. (for-each
  16801. (lambda (x)
  16802. (when (and (pair? x)
  16803. (eq? (car x) 'unquote))
  16804. (lint-walk caller (cadr x) env) ; register refs
  16805. (set! happy #f)))
  16806. form)
  16807. ;; (begin (define x 1) `#(,x))
  16808. (if (not happy) ; these are used exactly 4 times (in a test suite!) in 2 million lines of open source scheme code
  16809. (lint-format "quasiquoted vectors are not supported: ~A" caller form)))
  16810. ;; `(x #(,x)) for example will not work in s7, but `(,x ,(vector x)) will
  16811. env)
  16812. (else
  16813. env)))
  16814. ;; -------- lint-file --------
  16815. (define *report-input* #t)
  16816. ;; lint-file is called via load etc above and it's a pain to thread this variable all the way down the call chain
  16817. (define (lint-file-1 file env)
  16818. (set! linted-files (cons file linted-files))
  16819. (let ((fp (if (input-port? file)
  16820. file
  16821. (begin
  16822. (set! *current-file* file)
  16823. (catch #t
  16824. (lambda ()
  16825. (let ((p (open-input-file file)))
  16826. (when *report-input*
  16827. (format outport
  16828. (if (and (output-port? outport)
  16829. (not (memq outport (list *stderr* *stdout*))))
  16830. (values "~%~NC~%;~A~%" (+ lint-left-margin 16) #\-)
  16831. ";~A~%")
  16832. file))
  16833. p))
  16834. (lambda args
  16835. (format outport "~NCcan't open ~S: ~A~%" lint-left-margin #\space file (apply format #f (cadr args)))
  16836. #f))))))
  16837. (when (input-port? fp)
  16838. (do ((vars env)
  16839. (line 0)
  16840. (last-form #f)
  16841. (last-line-number -1)
  16842. (form (read fp) (read fp)))
  16843. ((eof-object? form)
  16844. (if (not (input-port? file))
  16845. (close-input-port fp))
  16846. (when *report-repeated-code-fragments*
  16847. (do ((i 6 (+ i 1)))
  16848. ((= i *fragments-size*))
  16849. (when (> (hash-table-entries (fragments i)) 0)
  16850. (let ((v (copy (fragments i) (make-vector (hash-table-entries (fragments i)))))) ; (key . vector)
  16851. (for-each (lambda (a1)
  16852. (let ((a (cdr a1)))
  16853. (when (> (vector-ref a 0) 1)
  16854. (vector-set! a 1 (map (lambda (b)
  16855. (if (< 0 b 100000)
  16856. b
  16857. (values)))
  16858. (reverse (vector-ref a 1)))))))
  16859. v)
  16860. (for-each (lambda (keyval)
  16861. (let ((val (cdr keyval)))
  16862. (if (and (>= (vector-ref val 0) 2)
  16863. (> (* (vector-ref val 0) (vector-ref val 0) i) 100)) ; 120 seems too high
  16864. (if (equal? (vector-ref val 3) (car keyval))
  16865. (format outport "~NC~A uses, size: ~A, lines: '~A):~%~NCexpression: ~A~%"
  16866. lint-left-margin #\space
  16867. (vector-ref val 0) i (vector-ref val 1)
  16868. (+ lint-left-margin 2) #\space
  16869. (truncated-list->string (car keyval)))
  16870. (format outport "~NC~A uses, size: ~A, lines: '~A):~%~NCpattern: ~A~%~NCexample: ~A~%"
  16871. lint-left-margin #\space
  16872. (vector-ref val 0) i (vector-ref val 1)
  16873. (+ lint-left-margin 2) #\space
  16874. (truncated-list->string (car keyval))
  16875. (+ lint-left-margin 2) #\space
  16876. (truncated-list->string (vector-ref val 3)))))))
  16877. (sort! v (lambda (kv1 kv2)
  16878. (let ((a (cdr kv1))
  16879. (b (cdr kv2)))
  16880. (or (> (vector-ref a 0) (vector-ref b 0))
  16881. (and (= (vector-ref a 0) (vector-ref b 0))
  16882. (string<? (or (vector-ref a 4)
  16883. (vector-set! a 4 (object->string (vector-ref a 3))))
  16884. (or (vector-ref b 4)
  16885. (vector-set! b 4 (object->string (vector-ref b 3))))))))))))))))
  16886. (if (pair? form)
  16887. (set! line (max line (pair-line-number form))))
  16888. (if (not (or (= last-line-number -1)
  16889. (side-effect? last-form vars)))
  16890. (format outport "~NCtop-level (line ~D): this has no effect: ~A~%"
  16891. lint-left-margin #\space last-line-number
  16892. (truncated-list->string last-form)))
  16893. (set! last-form form)
  16894. (set! last-line-number line)
  16895. (if (and (pair? form)
  16896. (memq (car form) '(define define-macro))
  16897. (pair? (cdr form))
  16898. (pair? (cadr form)))
  16899. (let ((f (caadr form)))
  16900. (if (and (symbol? f)
  16901. (hash-table-ref built-in-functions f))
  16902. (format outport "~NCtop-level ~Aredefinition of built-in function ~A: ~A~%"
  16903. lint-left-margin #\space
  16904. (if (> (pair-line-number form) 0)
  16905. (format #f "(line ~D) " (pair-line-number form))
  16906. "")
  16907. f (truncated-list->string form)))))
  16908. (set! vars (lint-walk (if (symbol? form)
  16909. form
  16910. (and (pair? form)
  16911. (car form)))
  16912. form
  16913. vars))))))
  16914. (define (lint-file file env)
  16915. ;; (if (string? file) (format *stderr* "lint ~S~%" file))
  16916. (if (member file linted-files)
  16917. env
  16918. (let ((old-current-file *current-file*)
  16919. (old-pp-left-margin pp-left-margin)
  16920. (old-lint-left-margin lint-left-margin)
  16921. (old-load-path *load-path*))
  16922. (dynamic-wind
  16923. (lambda ()
  16924. (set! pp-left-margin (+ pp-left-margin 4))
  16925. (set! lint-left-margin (+ lint-left-margin 4))
  16926. (when (and (string? file)
  16927. (char=? (file 0) #\/))
  16928. (let ((last-pos 0))
  16929. (do ((pos (char-position #\/ file (+ last-pos 1)) (char-position #\/ file (+ last-pos 1))))
  16930. ((not pos)
  16931. (if (> last-pos 0)
  16932. (set! *load-path* (cons (substring file 0 last-pos) *load-path*))))
  16933. (set! last-pos pos)))))
  16934. (lambda ()
  16935. (lint-file-1 file env))
  16936. (lambda ()
  16937. (set! pp-left-margin old-pp-left-margin)
  16938. (set! lint-left-margin old-lint-left-margin)
  16939. (set! *current-file* old-current-file)
  16940. (set! *load-path* old-load-path)
  16941. (if (positive? (length *current-file*))
  16942. (newline outport)))))))
  16943. ;;; --------------------------------------------------------------------------------'
  16944. ;;; lint itself
  16945. ;;;
  16946. (let ((documentation "(lint file port) looks for infelicities in file's scheme code")
  16947. (signature (list #t string? output-port? boolean?)))
  16948. (lambda* (file (outp *output-port*) (report-input #t))
  16949. (set! outport outp)
  16950. (set! other-identifiers (make-hash-table))
  16951. (set! linted-files ())
  16952. (fill! other-names-counts 0)
  16953. (do ((i 0 (+ i 1)))
  16954. ((= i *fragments-size*))
  16955. (fill! (fragments i) #f))
  16956. (set! last-simplify-boolean-line-number -1)
  16957. (set! last-simplify-numeric-line-number -1)
  16958. (set! last-simplify-cxr-line-number -1)
  16959. (set! last-checker-line-number -1)
  16960. (set! last-cons-line-number -1)
  16961. (set! last-if-line-number -1)
  16962. (set! last-rewritten-internal-define #f)
  16963. (set! line-number -1)
  16964. (set! quote-warnings 0)
  16965. (set! pp-left-margin 0)
  16966. (set! lint-left-margin -3) ; lint-file above adds 4
  16967. (set! big-constants (make-hash-table))
  16968. (set! *report-input* report-input)
  16969. (set! *report-nested-if* (if (integer? *report-nested-if*) (max 3 *report-nested-if*) 4))
  16970. (set! *report-short-branch* (if (integer? *report-short-branch*) (max 0 *report-short-branch*) 12))
  16971. (set! *#readers*
  16972. (list (cons #\e (lambda (str)
  16973. (unless (string=? str "e")
  16974. (let ((num (string->number (substring str 1))))
  16975. (cond ((not num))
  16976. ((rational? num)
  16977. (format outport "~NCthis #e is dumb, #~A -> ~A~%" lint-left-margin #\space str (substring str 1)))
  16978. ((not (real? num))
  16979. (format outport "~NC#e can't handle complex numbers, #~A -> ~A~%" lint-left-margin #\space str num))
  16980. ((= num (floor num))
  16981. (format outport "~NCperhaps #~A -> ~A~%" lint-left-margin #\space str (floor num))))))
  16982. #f))
  16983. (cons #\i (lambda (str)
  16984. (unless (string=? str "i")
  16985. (let ((num (string->number (substring str 1))))
  16986. (when num
  16987. (format outport
  16988. (if (not (rational? num))
  16989. (values "~NCthis #i is dumb, #~A -> ~A~%" lint-left-margin #\space str (substring str 1))
  16990. (values "~NCperhaps #~A -> ~A~%" lint-left-margin #\space str (* 1.0 num)))))))
  16991. #f))
  16992. (cons #\d (lambda (str)
  16993. (if (and (not (string=? str "d"))
  16994. (string->number (substring str 1)))
  16995. (format outport "~NC#d is pointless, #~A -> ~A~%" lint-left-margin #\space str (substring str 1)))
  16996. #f))
  16997. (cons #\' (lambda (str) ; for Guile (and syntax-rules, I think)
  16998. (list 'syntax (if (string=? str "'") (read) (string->symbol str)))))
  16999. (cons #\` (lambda (str) ; for Guile (sigh)
  17000. (list 'quasisyntax (if (string=? str "'") (read) (string->symbol str)))))
  17001. (cons #\, (lambda (str) ; the same, the last is #,@ -> unsyntax-splicing -- right.
  17002. (list 'unsyntax (if (string=? str "'") (read) (string->symbol str)))))
  17003. (cons #\& (lambda (str) ; ancient Guile code
  17004. (make-keyword (substring str 1))))
  17005. (cons #\\ (lambda (str)
  17006. (cond ((assoc str '(("\\x0" . #\null)
  17007. ("\\x7" . #\alarm)
  17008. ("\\x8" . #\backspace)
  17009. ("\\x9" . #\tab)
  17010. ("\\xd" . #\return)
  17011. ("\\xa" . #\newline)
  17012. ("\\1b" . #\escape)
  17013. ("\\x20" . #\space)
  17014. ("\\x7f" . #\delete)))
  17015. => (lambda (c)
  17016. (format outport "~NC#\\~A is ~W~%" lint-left-margin #\space (substring str 1) (cdr c)))))
  17017. #f))
  17018. (cons #\! (lambda (str)
  17019. (if (member str '("!optional" "!default" "!rest" "!key" "!aux" "!false" "!true" "!r6rs") string-ci=?) ; for MIT-scheme
  17020. (make-keyword (substring str 1))
  17021. (let ((lc (str 0))) ; s7 should handle this, but...
  17022. (do ((c (read-char) (read-char)))
  17023. ((or (and (eof-object? c)
  17024. (or (format outport "~NCunclosed block comment~%" lint-left-margin #\space)
  17025. #t))
  17026. (and (char=? lc #\!)
  17027. (char=? c #\#)))
  17028. #f)
  17029. (set! lc c))))))))
  17030. ;; try to get past all the # and \ stuff in other Schemes
  17031. ;; main remaining problem: [] used as parentheses (Gauche and Chicken for example)
  17032. (set! (hook-functions *read-error-hook*)
  17033. (list (lambda (h)
  17034. (let ((data (h 'data))
  17035. (line (port-line-number)))
  17036. (if (not (h 'type))
  17037. (begin
  17038. (format outport "~NCreader[~A]: unknown \\ usage: \\~C~%" lint-left-margin #\space line data)
  17039. (set! (h 'result) data))
  17040. (begin
  17041. (format outport "~NCreader[~A]: unknown # object: #~A~%" lint-left-margin #\space line data)
  17042. (set! (h 'result)
  17043. (catch #t
  17044. (lambda ()
  17045. (case (data 0)
  17046. ((#\;) (read) (values))
  17047. ((#\T) (string=? data "T"))
  17048. ((#\F) (and (string=? data "F") ''#f))
  17049. ((#\X #\B #\O #\D)
  17050. (let ((num (string->number (substring data 1) (case (data 0) ((#\X) 16) ((#\O) 8) ((#\B) 2) ((#\D) 10)))))
  17051. (if (number? num)
  17052. (begin
  17053. (format outport "~NCuse #~A~A not #~A~%"
  17054. lint-left-margin #\space
  17055. (char-downcase (data 0)) (substring data 1) data)
  17056. num)
  17057. (string->symbol data))))
  17058. ((#\l #\z)
  17059. (let ((num (string->number (substring data 1)))) ; Bigloo (also has #ex #lx #z and on and on)
  17060. (if (number? num)
  17061. (begin
  17062. (format outport "~NCjust omit this silly #~C!~%" lint-left-margin #\space (data 0))
  17063. num)
  17064. (string->symbol data))))
  17065. ((#\u) ; for Bigloo
  17066. (if (string=? data "unspecified")
  17067. (format outport "~NCuse #<unspecified>, not #unspecified~%" lint-left-margin #\space))
  17068. ;; #<unspecified> seems to hit the no-values check?
  17069. (string->symbol data))
  17070. ;; Bigloo also seems to use #" for here-doc concatenation??
  17071. ((#\v) ; r6rs byte-vectors?
  17072. (if (string=? data "vu8")
  17073. (format outport "~NCuse #u8 in s7, not #vu8~%" lint-left-margin #\space))
  17074. (string->symbol data))
  17075. ((#\>) ; for Chicken, apparently #>...<# encloses in-place C code
  17076. (do ((last #\#)
  17077. (c (read-char) (read-char)))
  17078. ((and (char=? last #\<)
  17079. (char=? c #\#))
  17080. (values))
  17081. (if (char=? c #\newline)
  17082. (set! (port-line-number ()) (+ (port-line-number) 1)))
  17083. (set! last c)))
  17084. ((#\<) ; Chicken also, #<<EOF -> EOF
  17085. (if (and (char=? (data 1) #\<)
  17086. (> (length data) 2))
  17087. (do ((end (substring data 2))
  17088. (c (read-line) (read-line)))
  17089. ((string-position end c)
  17090. (values)))
  17091. (string->symbol data)))
  17092. ((#\\)
  17093. (cond ((assoc data '(("\\newline" . #\newline)
  17094. ("\\return" . #\return)
  17095. ("\\space" . #\space)
  17096. ("\\tab" . #\tab)
  17097. ("\\null" . #\null)
  17098. ("\\nul" . #\null)
  17099. ("\\linefeed" . #\linefeed)
  17100. ("\\alarm" . #\alarm)
  17101. ("\\esc" . #\escape)
  17102. ("\\escape" . #\escape)
  17103. ("\\rubout" . #\delete)
  17104. ("\\delete" . #\delete)
  17105. ("\\backspace" . #\backspace)
  17106. ("\\page" . #\xc)
  17107. ("\\altmode" . #\escape)
  17108. ("\\bel" . #\alarm) ; #\x07
  17109. ("\\sub" . #\x1a)
  17110. ("\\soh" . #\x01)
  17111. ;; these are for Guile
  17112. ("\\vt" . #\xb)
  17113. ("\\bs" . #\backspace)
  17114. ("\\cr" . #\newline)
  17115. ("\\sp" . #\space)
  17116. ("\\lf" . #\linefeed)
  17117. ("\\nl" . #\null)
  17118. ("\\ht" . #\tab)
  17119. ("\\ff" . #\xc)
  17120. ("\\np" . #\xc))
  17121. string-ci=?)
  17122. => (lambda (c)
  17123. (format outport "~NCperhaps use ~W instead~%" (+ lint-left-margin 4) #\space (cdr c))
  17124. (cdr c)))
  17125. (else
  17126. (string->symbol (substring data 1)))))
  17127. (else
  17128. (string->symbol data))))
  17129. (lambda args #f)))))))))
  17130. ;; preset list-tail and list-ref
  17131. (hash-table-set! (fragments 10) '((if (zero? _2_) _1_ (_F_ (cdr _1_) (- _2_ 1))))
  17132. (vector 0 ()
  17133. (list (cons 'list-tail
  17134. (inlet :initial-value '(define (list-tail x k) (if (zero? k) x (list-tail (cdr x) (- k 1))))
  17135. :arglist '(x k)
  17136. :history :built-in)))
  17137. '(define (list-tail x k) (if (zero? k) x (list-tail (cdr x) (- k 1))))
  17138. #f))
  17139. (hash-table-set! (fragments 12) '((if (= _2_ 0) (car _1_) (_F_ (cdr _1_) (- _2_ 1))))
  17140. (vector 0 ()
  17141. (list (cons 'list-ref (inlet :initial-value '(define (list-ref items n) (if (= n 0) (car items) (list-ref (cdr items) (- n 1))))
  17142. :arglist '(items n)
  17143. :history :built-in)))
  17144. '(define (list-ref items n) (if (= n 0) (car items) (list-ref (cdr items) (- n 1))))
  17145. #f))
  17146. ;; -------- call lint --------
  17147. (let ((vars (lint-file file ())))
  17148. (set! lint-left-margin (max lint-left-margin 1))
  17149. (when (pair? vars)
  17150. (if *report-multiply-defined-top-level-functions*
  17151. (for-each
  17152. (lambda (var)
  17153. (let ((var-file (hash-table-ref *top-level-objects* (car var))))
  17154. (if (not var-file)
  17155. (hash-table-set! *top-level-objects* (car var) *current-file*)
  17156. (if (and (string? *current-file*)
  17157. (not (string=? var-file *current-file*)))
  17158. (format outport "~NC~S is defined at the top level in ~S and ~S~%"
  17159. lint-left-margin #\space
  17160. (car var) var-file *current-file*)))))
  17161. vars))
  17162. (if (string? file)
  17163. (report-usage top-level: "" vars vars))))
  17164. (for-each
  17165. (lambda (p)
  17166. (if (or (> (cdr p) 5)
  17167. (and (> (cdr p) 3)
  17168. (> (length (car p)) 12)))
  17169. (format outport "~A~A occurs ~D times~%"
  17170. (if (pair? (car p)) "'" "")
  17171. (truncated-list->string (car p)) (cdr p))))
  17172. big-constants)
  17173. (if (and *report-undefined-identifiers*
  17174. (positive? (hash-table-entries other-identifiers)))
  17175. (let ((lst (sort! (map car other-identifiers) (lambda (a b)
  17176. (string<? (symbol->string a) (symbol->string b))))))
  17177. (format outport "~NCth~A identifier~A not defined~A: ~{~S~^ ~}~%"
  17178. lint-left-margin #\space
  17179. (if (= (hash-table-entries other-identifiers) 1)
  17180. (values "is" " was")
  17181. (values "e following" "s were"))
  17182. (if (string? file) (format #f " in ~S" file) "")
  17183. lst)
  17184. (fill! other-identifiers #f)))))))
  17185. ;;; --------------------------------------------------------------------------------
  17186. ;;; this reads an HTML file, finds likely-looking scheme code, and runs lint over it.
  17187. ;;; called on all snd files in hg.scm
  17188. (define (html-lint file)
  17189. (define (remove-markups str)
  17190. (let ((tpos (string-position "<b>" str)))
  17191. (if tpos
  17192. (let ((epos (string-position "</b>" str)))
  17193. (remove-markups (string-append (substring str 0 tpos)
  17194. (substring str (+ tpos 3) epos)
  17195. (substring str (+ epos 4)))))
  17196. (let ((apos (string-position "<a " str))
  17197. (epos (string-position "<em " str)))
  17198. (if (not (or apos epos))
  17199. str
  17200. (let* ((pos ((if (and apos epos) min or) apos epos))
  17201. (bpos (+ (char-position #\> str (+ pos 1)) 1))
  17202. (epos (string-position (if (and apos (= pos apos)) "</a>" "</em>") str bpos)))
  17203. (string-append (substring str 0 pos)
  17204. (substring str bpos epos)
  17205. (remove-markups (substring str (+ epos (if (and apos (= apos pos)) 4 5)))))))))))
  17206. (define (fixup-html str)
  17207. (let ((pos (char-position #\& str)))
  17208. (if (not pos)
  17209. str
  17210. (string-append (substring str 0 pos)
  17211. (let* ((epos (char-position #\; str pos))
  17212. (substr (substring str (+ pos 1) epos)))
  17213. (string-append (cond ((assoc substr '(("gt" . ">")
  17214. ("lt" . "<")
  17215. ("mdash" . "-")
  17216. ("amp" . "&"))
  17217. string=?) => cdr)
  17218. (else (format #t "unknown: ~A~%" substr)))
  17219. (fixup-html (substring str (+ epos 1)))))))))
  17220. (call-with-input-file file
  17221. (lambda (f)
  17222. (do ((line-num 0 (+ line-num 1))
  17223. (line (read-line f #t) (read-line f #t)))
  17224. ((eof-object? line))
  17225. ;; look for <pre , gather everything until </pre>
  17226. ;; decide if it is scheme code (first char is #\()
  17227. ;; if so, clean out html markup stuff, call lint on that
  17228. (let ((pos (string-position "<pre" line)))
  17229. (when pos
  17230. (let ((code (substring line (+ (char-position #\> line) 1))))
  17231. (do ((cline (read-line f #t) (read-line f #t))
  17232. (rline 1 (+ rline 1)))
  17233. ((string-position "</pre>" cline)
  17234. (set! line-num (+ line-num rline)))
  17235. (set! code (string-append code cline)))
  17236. ;; is first non-whitespace char #\(? ignoring comments
  17237. (do ((len (length code))
  17238. (i 0 (+ i 1)))
  17239. ((>= i len))
  17240. (let ((c (string-ref code i)))
  17241. (unless (char-whitespace? c)
  17242. (if (char=? c #\;)
  17243. (set! i (char-position #\newline code i))
  17244. (begin
  17245. (set! i (+ len 1))
  17246. (when (char=? c #\()
  17247. (catch #t
  17248. (lambda ()
  17249. (let ((outstr (call-with-output-string
  17250. (lambda (op)
  17251. (call-with-input-string
  17252. (object->string (with-input-from-string
  17253. (fixup-html (remove-markups code))
  17254. read)
  17255. #t) ; write, not display
  17256. (lambda (ip)
  17257. (let-temporarily ((*report-shadowed-variables* #t))
  17258. (lint ip op #f))))))))
  17259. (if (> (length outstr) 1) ; possible newline at end
  17260. (format () ";~A ~D: ~A~%" file line-num outstr))))
  17261. (lambda args
  17262. (format () ";~A ~D, error in read: ~A ~A~%" file line-num args
  17263. (fixup-html (remove-markups code))))))))))))))))))
  17264. ;;; --------------------------------------------------------------------------------
  17265. ;;; and this reads C code looking for s7_eval_c_string. No attempt here to
  17266. ;;; handle weird cases.
  17267. (define (C-lint file)
  17268. (call-with-input-file file
  17269. (lambda (f)
  17270. (do ((line-num 0 (+ line-num 1))
  17271. (line (read-line f #t) (read-line f #t)))
  17272. ((eof-object? line))
  17273. ;; look for s7_eval_c_string, get string arg without backslashes, call lint
  17274. (let ((pos (string-position "s7_eval_c_string(sc, \"(" line)))
  17275. (when pos
  17276. (let ((code (substring line (+ pos (length "s7_eval_c_string(sc, \"")))))
  17277. (if (not (string-position "\");" code))
  17278. (do ((cline (read-line f #t) (read-line f #t))
  17279. (rline 1 (+ rline 1)))
  17280. ((string-position "\");" cline)
  17281. (set! code (string-append code cline))
  17282. (set! line-num (+ line-num rline)))
  17283. (set! code (string-append code cline))))
  17284. (let ((len (string-position "\");" code)))
  17285. (set! code (substring code 0 len))
  17286. ;; clean out backslashes
  17287. (do ((i 0 (+ i 1)))
  17288. ((>= i (- len 3)))
  17289. (cond ((not (char=? (code i) #\\)))
  17290. ((char=? (code (+ i 1)) #\n)
  17291. (set! (code i) #\space)
  17292. (set! (code (+ i 1)) #\space))
  17293. ((memv (code (+ i 1)) '(#\newline #\"))
  17294. (set! (code i) #\space))
  17295. ((and (char=? (code (+ i 1)) #\\)
  17296. (char=? (code (- i 1)) #\#))
  17297. (set! (code (- i 1)) #\space)
  17298. (set! (code i) #\#)))))
  17299. (catch #t
  17300. (lambda ()
  17301. (let ((outstr (call-with-output-string
  17302. (lambda (op)
  17303. (call-with-input-string code
  17304. (lambda (ip)
  17305. (let-temporarily ((*report-shadowed-variables* #t))
  17306. (lint ip op #f))))))))
  17307. (if (> (length outstr) 1) ; possible newline at end
  17308. (format () ";~A ~D: ~A~%" file line-num outstr))))
  17309. (lambda args
  17310. (format () ";~A ~D, error in read: ~A ~A~%" file line-num args code))))))))))
  17311. ;;; --------------------------------------------------------------------------------
  17312. #|
  17313. ;;; external use of lint contents (see also snd-lint.scm):
  17314. (for-each (lambda (f)
  17315. (if (not (hash-table-ref (*lint* 'no-side-effect-functions) (car f)))
  17316. (format *stderr* "~A " (car f))))
  17317. (*lint* 'built-in-functions))
  17318. ;;; get rid of []'s! (using Snd)
  17319. (define (edit file)
  17320. (let* ((str (file->string file))
  17321. (len (length str)))
  17322. (do ((i 0 (+ i 1)))
  17323. ((= i len))
  17324. (case (str i)
  17325. ((#\]) (set! (str i) #\)))
  17326. ((#\[) (set! (str i) #\())))
  17327. (call-with-output-file file
  17328. (lambda (p)
  17329. (display str p)))
  17330. #f))
  17331. |#
  17332. ;;; fragments:
  17333. ;;; perhaps for fragment hash-ref (list fragment) to find function?
  17334. ;;; and check leading cases for all bodies? -- would need to handle this in reduce-tree walker?
  17335. ;;; need any-match arg nums (a 2nd level match)
  17336. ;;; if 2-arg func, reversed -> nth for list-ref -- need reversal signal
  17337. ;;; this is tricky (initial code in tmp) -- if recursive call, need args reversed so check shadowing etc
  17338. ;;; if several fragments share the same code, report just the biggest, and maybe give the _n_ values for at least the example?
  17339. ;;; maybe divide the trigger by the _n_ top? (need to save this number)
  17340. ;;;
  17341. ;;; blocks:
  17342. ;;; reduce-dependencies -- look for blocks with restricted outer vars, make func and add to closure, check for func-reuse
  17343. ;;; but this collides with current 1-call->embedded code in lint-walk-body unless we use the closure
  17344. ;;; so... perhaps use out-vars to get names -- if < 5, func? (if any out-var set, quit)
  17345. ;;; perhaps start with if branches, when/unless
  17346. ;;;
  17347. ;;; unused var search made smarter (in any body+locals)
  17348. ;;; named-let + map init ->embed as in map+map [do does not happen usefully]
  17349. ;;; where <expr> assumed <expr>, or where <expr> set to <expr> or assert <expr> and report violations [expr=pattern here]
  17350. ;;; 184 25029 665340