Add compiler warning for possibly unbound variables.
* module/language/tree-il/analyze.scm (<toplevel-info>): New record
type.
(env-module, report-possibly-unbound-variables): New procedures.
* module/language/tree-il/compile-glil.scm (%warning-passes): Add
`unbound-variable'.
* module/system/base/message.scm (%warning-types): Likewise.
* test-suite/tests/tree-il.test (read-and-compile, %opts-w-unbound):
New.
("warnings")["unbound variable"]: New test prefix.
This commit is contained in:
parent
43eb8acada
commit
f67ddf9dbf
4 changed files with 171 additions and 3 deletions
|
|
@ -24,8 +24,11 @@
|
|||
#:use-module (system base syntax)
|
||||
#:use-module (system base message)
|
||||
#:use-module (language tree-il)
|
||||
#:use-module ((system base compile)
|
||||
#:select (current-compilation-environment))
|
||||
#:export (analyze-lexicals
|
||||
report-unused-variables))
|
||||
report-unused-variables
|
||||
report-possibly-unbound-variables))
|
||||
|
||||
;; Allocation is the process of assigning storage locations for lexical
|
||||
;; variables. A lexical variable has a distinct "address", or storage
|
||||
|
|
@ -615,3 +618,98 @@
|
|||
(make-binding-info '() '() '())
|
||||
tree)
|
||||
tree)
|
||||
|
||||
|
||||
;;;
|
||||
;;; Unbound variable analysis.
|
||||
;;;
|
||||
|
||||
;; <toplevel-info> records are used during tree traversal in search of
|
||||
;; possibly unbound variable. They contain a list of references to
|
||||
;; potentially unbound top-level variables, a list of the top-level defines
|
||||
;; that have been encountered, and a "location stack" (see above).
|
||||
(define-record-type <toplevel-info>
|
||||
(make-toplevel-info refs defs locs)
|
||||
toplevel-info?
|
||||
(refs toplevel-info-refs) ;; ((VARIABLE-NAME . LOCATION) ...)
|
||||
(defs toplevel-info-defs) ;; (VARIABLE-NAME ...)
|
||||
(locs toplevel-info-locs)) ;; (LOCATION ...)
|
||||
|
||||
(define (env-module e)
|
||||
"Return the module corresponding to E."
|
||||
;; XXX: This is a bit of a hack since since representation of compile-time
|
||||
;; environments is hidden in `(language scheme compile-tree-il)'.
|
||||
(cond ((pair? e) (car e))
|
||||
((module? e) e)
|
||||
(else (current-compilation-environment))))
|
||||
|
||||
;; TODO: Combine with `report-unused-variables' so we don't traverse the tree
|
||||
;; once for each warning type.
|
||||
|
||||
(define (report-possibly-unbound-variables tree env)
|
||||
"Return possibly unbound variables in TREE. Return TREE."
|
||||
(define toplevel
|
||||
(let ((env (env-module env)))
|
||||
(tree-il-fold (lambda (x info)
|
||||
;; X is a leaf: extend INFO's refs accordingly.
|
||||
(let ((refs (toplevel-info-refs info))
|
||||
(defs (toplevel-info-defs info))
|
||||
(locs (toplevel-info-locs info)))
|
||||
(define (bound? name)
|
||||
(or (and (module? env)
|
||||
(module-variable env name))
|
||||
(memq name defs)))
|
||||
|
||||
(record-case x
|
||||
((<toplevel-ref> name src)
|
||||
(if (bound? name)
|
||||
info
|
||||
(let ((src (or src (find pair? locs))))
|
||||
(make-toplevel-info (alist-cons name src refs)
|
||||
defs
|
||||
locs))))
|
||||
(else info))))
|
||||
|
||||
(lambda (x info)
|
||||
;; Going down into X.
|
||||
(let* ((refs (toplevel-info-refs info))
|
||||
(defs (toplevel-info-defs info))
|
||||
(src (tree-il-src x))
|
||||
(locs (cons src (toplevel-info-locs info))))
|
||||
(define (bound? name)
|
||||
(or (and (module? env)
|
||||
(module-variable env name))
|
||||
(memq name defs)))
|
||||
|
||||
(record-case x
|
||||
((<toplevel-set> name src)
|
||||
(if (bound? name)
|
||||
(make-toplevel-info refs defs locs)
|
||||
(let ((src (find pair? locs)))
|
||||
(make-toplevel-info (alist-cons name src refs)
|
||||
defs
|
||||
locs))))
|
||||
((<toplevel-define> name)
|
||||
(make-toplevel-info (alist-delete name refs eq?)
|
||||
(cons name defs)
|
||||
locs))
|
||||
(else
|
||||
(make-toplevel-info refs defs locs)))))
|
||||
|
||||
(lambda (x info)
|
||||
;; Leaving X's scope.
|
||||
(let ((refs (toplevel-info-refs info))
|
||||
(defs (toplevel-info-defs info))
|
||||
(locs (toplevel-info-locs info)))
|
||||
(make-toplevel-info refs defs (cdr locs))))
|
||||
|
||||
(make-toplevel-info '() '() '())
|
||||
tree)))
|
||||
|
||||
(for-each (lambda (name+loc)
|
||||
(let ((name (car name+loc))
|
||||
(loc (cdr name+loc)))
|
||||
(warning 'unbound-variable loc name)))
|
||||
(reverse (toplevel-info-refs toplevel)))
|
||||
|
||||
tree)
|
||||
|
|
|
|||
|
|
@ -46,7 +46,8 @@
|
|||
(define *comp-module* (make-fluid))
|
||||
|
||||
(define %warning-passes
|
||||
`((unused-variable . ,report-unused-variables)))
|
||||
`((unused-variable . ,report-unused-variables)
|
||||
(unbound-variable . ,report-possibly-unbound-variables)))
|
||||
|
||||
(define (compile-glil x e opts)
|
||||
(define warnings
|
||||
|
|
|
|||
|
|
@ -79,6 +79,12 @@
|
|||
"report unused variables"
|
||||
,(lambda (port loc name)
|
||||
(format port "~A: warning: unused variable `~A'~%"
|
||||
loc name)))
|
||||
|
||||
(unbound-variable
|
||||
"report possibly unbound variables"
|
||||
,(lambda (port loc name)
|
||||
(format port "~A: warning: possibly unbound variable `~A'~%"
|
||||
loc name))))))
|
||||
|
||||
(define (lookup-warning-type name)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue