mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-04-30 03:40:34 +02:00
Add -Wunused-module.
* module/language/tree-il/analyze.scm (<module-info>): New record type. (unused-module-analysis): New variable. (make-unused-module-analysis): New analysis. (make-analyzer): Add it. * module/system/base/message.scm (%warning-types): Add 'unused-module'. * test-suite/tests/tree-il.test (%opts-w-unused-module): New variable. ("warnings")["unused-module"]: New test prefix. * NEWS: Update.
This commit is contained in:
parent
821e0f9cd5
commit
89c3bae3cf
4 changed files with 336 additions and 3 deletions
17
NEWS
17
NEWS
|
@ -4,6 +4,23 @@ See the end for copying conditions.
|
||||||
|
|
||||||
Please send Guile bug reports to bug-guile@gnu.org.
|
Please send Guile bug reports to bug-guile@gnu.org.
|
||||||
|
|
||||||
|
|
||||||
|
Changes in 3.0.10 (since 3.0.9)
|
||||||
|
|
||||||
|
* New interfaces and functionality
|
||||||
|
|
||||||
|
** New warning: unused-module
|
||||||
|
|
||||||
|
This analysis, enabled at `-W2', issues warnings for modules that appear
|
||||||
|
in a `use-modules' form or as a #:use-module clause of `define-module',
|
||||||
|
and whose bindings are unused. This is useful to trim the list of
|
||||||
|
imports of a module.
|
||||||
|
|
||||||
|
In some cases, the compiler cannot conclude whether a module is
|
||||||
|
definitely unused---this is notably the case for modules that are only
|
||||||
|
used at macro-expansion time, such as (srfi srfi-26). In those cases,
|
||||||
|
the compiler reports it as "possibly unused".
|
||||||
|
|
||||||
|
|
||||||
Changes in 3.0.9 (since 3.0.8)
|
Changes in 3.0.9 (since 3.0.8)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
;;; Diagnostic warnings for Tree-IL
|
;;; Diagnostic warnings for Tree-IL
|
||||||
|
|
||||||
;; Copyright (C) 2001,2008-2014,2016,2018-2022 Free Software Foundation, Inc.
|
;; Copyright (C) 2001,2008-2014,2016,2018-2023 Free Software Foundation, Inc.
|
||||||
|
|
||||||
;;;; This library is free software; you can redistribute it and/or
|
;;;; This library is free software; you can redistribute it and/or
|
||||||
;;;; modify it under the terms of the GNU Lesser General Public
|
;;;; modify it under the terms of the GNU Lesser General Public
|
||||||
|
@ -334,6 +334,155 @@ given `tree-il' element."
|
||||||
|
|
||||||
(make-reference-graph vlist-null vlist-null #f))))
|
(make-reference-graph vlist-null vlist-null #f))))
|
||||||
|
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Unused module analysis.
|
||||||
|
;;;
|
||||||
|
|
||||||
|
;; Module uses and references to bindings of imported modules.
|
||||||
|
(define-record-type <module-info>
|
||||||
|
(module-info location qualified-references
|
||||||
|
toplevel-references toplevel-definitions)
|
||||||
|
module-info?
|
||||||
|
(location module-info-location) ;location vector | #f
|
||||||
|
(qualified-references module-info-qualified-references) ;module name vhash
|
||||||
|
(toplevel-references module-info-toplevel-references) ;list of symbols
|
||||||
|
(toplevel-definitions module-info-toplevel-definitions)) ;symbol vhash
|
||||||
|
|
||||||
|
(define unused-module-analysis
|
||||||
|
;; Report unused modules in the given tree.
|
||||||
|
(make-tree-analysis
|
||||||
|
(lambda (x info env locs)
|
||||||
|
;; Going down into X: extend INFO accordingly.
|
||||||
|
(match x
|
||||||
|
((or ($ <module-ref> loc module name)
|
||||||
|
($ <module-set> loc module name))
|
||||||
|
(let ((references (module-info-qualified-references info)))
|
||||||
|
(if (vhash-assoc module references)
|
||||||
|
info
|
||||||
|
(module-info (module-info-location info)
|
||||||
|
(vhash-cons module #t references)
|
||||||
|
(module-info-toplevel-references info)
|
||||||
|
(module-info-toplevel-definitions info)))))
|
||||||
|
((or ($ <toplevel-ref> loc module name)
|
||||||
|
($ <toplevel-set> loc module name))
|
||||||
|
(if (equal? module (module-name env))
|
||||||
|
(let ((references (module-info-toplevel-references info)))
|
||||||
|
(module-info (module-info-location info)
|
||||||
|
(module-info-qualified-references info)
|
||||||
|
(cons x references)
|
||||||
|
(module-info-toplevel-definitions info)))
|
||||||
|
(let ((references (module-info-qualified-references info)))
|
||||||
|
(module-info (module-info-location info)
|
||||||
|
(vhash-cons module #t references)
|
||||||
|
(module-info-toplevel-references info)
|
||||||
|
(module-info-toplevel-definitions info)))))
|
||||||
|
(($ <toplevel-define> loc module name)
|
||||||
|
(module-info (module-info-location info)
|
||||||
|
(module-info-qualified-references info)
|
||||||
|
(module-info-toplevel-references info)
|
||||||
|
(vhash-consq name x
|
||||||
|
(module-info-toplevel-definitions info))))
|
||||||
|
|
||||||
|
;; Record the approximate location of the module import. We
|
||||||
|
;; could parse the #:imports arguments to determine the location
|
||||||
|
;; of each #:use-module but we'll leave that as an exercise for
|
||||||
|
;; the reader.
|
||||||
|
(($ <call> loc ($ <module-ref> _ '(guile) 'define-module*))
|
||||||
|
(module-info loc
|
||||||
|
(module-info-qualified-references info)
|
||||||
|
(module-info-toplevel-references info)
|
||||||
|
(module-info-toplevel-definitions info)))
|
||||||
|
(($ <call> loc ($ <module-ref> _ '(guile) 'process-use-modules))
|
||||||
|
(module-info loc
|
||||||
|
(module-info-qualified-references info)
|
||||||
|
(module-info-toplevel-references info)
|
||||||
|
(module-info-toplevel-definitions info)))
|
||||||
|
|
||||||
|
(_
|
||||||
|
info)))
|
||||||
|
|
||||||
|
(lambda (x info env locs) ;leaving X's scope
|
||||||
|
info)
|
||||||
|
|
||||||
|
(lambda (info env) ;finishing
|
||||||
|
(define (defining-module ref env)
|
||||||
|
;; Return the name of the module that defines REF, a
|
||||||
|
;; <toplevel-ref> or <toplevel-set>, in ENV.
|
||||||
|
(let ((name (if (toplevel-ref? ref)
|
||||||
|
(toplevel-ref-name ref)
|
||||||
|
(toplevel-set-name ref))))
|
||||||
|
(match (vhash-assq name (module-info-toplevel-definitions info))
|
||||||
|
(#f
|
||||||
|
;; NAME is not among the top-level definitions of this
|
||||||
|
;; compilation unit, so check which module provides it.
|
||||||
|
(and=> (module-variable env name)
|
||||||
|
(lambda (variable)
|
||||||
|
(and=> (find (lambda (module)
|
||||||
|
(module-reverse-lookup module variable))
|
||||||
|
(module-uses env))
|
||||||
|
module-name))))
|
||||||
|
(_
|
||||||
|
(if (toplevel-ref? ref)
|
||||||
|
(toplevel-ref-mod ref)
|
||||||
|
(toplevel-set-mod ref))))))
|
||||||
|
|
||||||
|
(define (module-bindings-reexported? module env)
|
||||||
|
;; Return true if ENV reexports one or more bindings from MODULE.
|
||||||
|
(let ((module (resolve-interface module))
|
||||||
|
(tag (make-prompt-tag)))
|
||||||
|
(call-with-prompt tag
|
||||||
|
(lambda ()
|
||||||
|
(module-for-each (lambda (symbol variable)
|
||||||
|
(when (module-reverse-lookup module variable)
|
||||||
|
(abort-to-prompt tag)))
|
||||||
|
(module-public-interface env))
|
||||||
|
#f)
|
||||||
|
(const #t))))
|
||||||
|
|
||||||
|
(define (module-exports-macros? module)
|
||||||
|
;; Return #t if MODULE exports one or more macros.
|
||||||
|
(let ((tag (make-prompt-tag)))
|
||||||
|
(call-with-prompt tag
|
||||||
|
(lambda ()
|
||||||
|
(module-for-each (lambda (symbol variable)
|
||||||
|
(when (and (variable-bound? variable)
|
||||||
|
(macro?
|
||||||
|
(variable-ref variable)))
|
||||||
|
(abort-to-prompt tag)))
|
||||||
|
module)
|
||||||
|
#f)
|
||||||
|
(const #t))))
|
||||||
|
|
||||||
|
(let ((used-modules ;list of modules actually used
|
||||||
|
(fold (lambda (reference modules)
|
||||||
|
(let ((module (defining-module reference env)))
|
||||||
|
(if (or (not module) (vhash-assoc module modules))
|
||||||
|
modules
|
||||||
|
(vhash-cons module #t modules))))
|
||||||
|
(module-info-qualified-references info)
|
||||||
|
(module-info-toplevel-references info))))
|
||||||
|
|
||||||
|
;; Compare the modules imported by ENV with USED-MODULES, the
|
||||||
|
;; list of modules actually referenced. When a module is not in
|
||||||
|
;; USED-MODULES, check whether ENV reexports bindings from it.
|
||||||
|
(for-each (lambda (module)
|
||||||
|
(unless (or (vhash-assoc (module-name module)
|
||||||
|
used-modules)
|
||||||
|
(module-bindings-reexported?
|
||||||
|
(module-name module) env))
|
||||||
|
;; If MODULE exports macros, and if the expansion
|
||||||
|
;; of those macros doesn't contain <module-ref>s
|
||||||
|
;; inside MODULE, then we cannot conclude whether
|
||||||
|
;; or not MODULE is used.
|
||||||
|
(warning 'unused-module
|
||||||
|
(module-info-location info)
|
||||||
|
(module-name module)
|
||||||
|
(not (module-exports-macros? module)))))
|
||||||
|
(module-uses env))))
|
||||||
|
|
||||||
|
(module-info #f vlist-null '() vlist-null)))
|
||||||
|
|
||||||
|
|
||||||
;;;
|
;;;
|
||||||
;;; Shadowed top-level definition analysis.
|
;;; Shadowed top-level definition analysis.
|
||||||
|
@ -1268,6 +1417,8 @@ resort, return #t when EXP refers to the global variable SPECIAL-NAME."
|
||||||
#:level 3 #:kind unused-variable #:analysis unused-variable-analysis)
|
#:level 3 #:kind unused-variable #:analysis unused-variable-analysis)
|
||||||
(define-analysis make-unused-toplevel-analysis
|
(define-analysis make-unused-toplevel-analysis
|
||||||
#:level 2 #:kind unused-toplevel #:analysis unused-toplevel-analysis)
|
#:level 2 #:kind unused-toplevel #:analysis unused-toplevel-analysis)
|
||||||
|
(define-analysis make-unused-module-analysis
|
||||||
|
#:level 2 #:kind unused-module #:analysis unused-module-analysis)
|
||||||
(define-analysis make-shadowed-toplevel-analysis
|
(define-analysis make-shadowed-toplevel-analysis
|
||||||
#:level 2 #:kind shadowed-toplevel #:analysis shadowed-toplevel-analysis)
|
#:level 2 #:kind shadowed-toplevel #:analysis shadowed-toplevel-analysis)
|
||||||
(define-analysis make-arity-analysis
|
(define-analysis make-arity-analysis
|
||||||
|
@ -1287,6 +1438,7 @@ resort, return #t when EXP refers to the global variable SPECIAL-NAME."
|
||||||
(analysis (cons analysis tail)))))))
|
(analysis (cons analysis tail)))))))
|
||||||
(let ((analyses (compute-analyses make-unused-variable-analysis
|
(let ((analyses (compute-analyses make-unused-variable-analysis
|
||||||
make-unused-toplevel-analysis
|
make-unused-toplevel-analysis
|
||||||
|
make-unused-module-analysis
|
||||||
make-shadowed-toplevel-analysis
|
make-shadowed-toplevel-analysis
|
||||||
make-arity-analysis
|
make-arity-analysis
|
||||||
make-format-analysis
|
make-format-analysis
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
;;; User interface messages
|
;;; User interface messages
|
||||||
|
|
||||||
;; Copyright (C) 2009-2012,2016,2018,2020-2021 Free Software Foundation, Inc.
|
;; Copyright (C) 2009-2012,2016,2018,2020-2021,2023 Free Software Foundation, Inc.
|
||||||
|
|
||||||
;;; This library is free software; you can redistribute it and/or modify it
|
;;; This library is free software; you can redistribute it and/or modify it
|
||||||
;;; under the terms of the GNU Lesser General Public License as published by
|
;;; under the terms of the GNU Lesser General Public License as published by
|
||||||
|
@ -115,6 +115,15 @@
|
||||||
(emit port "~A: warning: possibly unused local top-level variable `~A'~%"
|
(emit port "~A: warning: possibly unused local top-level variable `~A'~%"
|
||||||
loc name)))
|
loc name)))
|
||||||
|
|
||||||
|
(unused-module
|
||||||
|
"report unused modules"
|
||||||
|
,(lambda (port loc name definitely-unused?)
|
||||||
|
(if definitely-unused?
|
||||||
|
(emit port "~A: warning: unused module ~a~%"
|
||||||
|
loc name)
|
||||||
|
(emit port "~A: warning: possibly unused module ~a~%"
|
||||||
|
loc name))))
|
||||||
|
|
||||||
(shadowed-toplevel
|
(shadowed-toplevel
|
||||||
"report shadowed top-level variables"
|
"report shadowed top-level variables"
|
||||||
,(lambda (port loc name previous-loc)
|
,(lambda (port loc name previous-loc)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
;;;; tree-il.test --- test suite for compiling tree-il -*- scheme -*-
|
;;;; tree-il.test --- test suite for compiling tree-il -*- scheme -*-
|
||||||
;;;; Andy Wingo <wingo@pobox.com> --- May 2009
|
;;;; Andy Wingo <wingo@pobox.com> --- May 2009
|
||||||
;;;;
|
;;;;
|
||||||
;;;; Copyright (C) 2009-2014,2018-2021 Free Software Foundation, Inc.
|
;;;; Copyright (C) 2009-2014,2018-2021,2023 Free Software Foundation, Inc.
|
||||||
;;;;
|
;;;;
|
||||||
;;;; This library is free software; you can redistribute it and/or
|
;;;; This library is free software; you can redistribute it and/or
|
||||||
;;;; modify it under the terms of the GNU Lesser General Public
|
;;;; modify it under the terms of the GNU Lesser General Public
|
||||||
|
@ -217,6 +217,9 @@
|
||||||
(define %opts-w-unused-toplevel
|
(define %opts-w-unused-toplevel
|
||||||
'(#:warnings (unused-toplevel)))
|
'(#:warnings (unused-toplevel)))
|
||||||
|
|
||||||
|
(define %opts-w-unused-module
|
||||||
|
'(#:warnings (unused-module)))
|
||||||
|
|
||||||
(define %opts-w-shadowed-toplevel
|
(define %opts-w-shadowed-toplevel
|
||||||
'(#:warnings (shadowed-toplevel)))
|
'(#:warnings (shadowed-toplevel)))
|
||||||
|
|
||||||
|
@ -414,6 +417,158 @@
|
||||||
#:to 'cps
|
#:to 'cps
|
||||||
#:opts %opts-w-unused-toplevel))))))
|
#:opts %opts-w-unused-toplevel))))))
|
||||||
|
|
||||||
|
(with-test-prefix "unused-module"
|
||||||
|
|
||||||
|
(pass-if-equal "quiet"
|
||||||
|
'()
|
||||||
|
(call-with-warnings
|
||||||
|
(lambda ()
|
||||||
|
(compile '(begin
|
||||||
|
(use-modules (ice-9 popen))
|
||||||
|
(define (proc cmd)
|
||||||
|
(open-input-pipe cmd)))
|
||||||
|
#:env (make-fresh-user-module)
|
||||||
|
#:opts %opts-w-unused-module))))
|
||||||
|
|
||||||
|
(pass-if-equal "quiet, renamer"
|
||||||
|
'()
|
||||||
|
(call-with-warnings
|
||||||
|
(lambda ()
|
||||||
|
(compile '(begin
|
||||||
|
(use-modules ((ice-9 popen) #:prefix p-))
|
||||||
|
(define (proc cmd)
|
||||||
|
(p-open-input-pipe cmd)))
|
||||||
|
#:env (make-fresh-user-module)
|
||||||
|
#:opts %opts-w-unused-module))))
|
||||||
|
|
||||||
|
(pass-if "definitely unused"
|
||||||
|
(let* ((defmod '(define-module (foo)
|
||||||
|
#:use-module (ice-9 vlist)
|
||||||
|
#:use-module (ice-9 popen)
|
||||||
|
#:export (proc)))
|
||||||
|
(w (call-with-warnings
|
||||||
|
(lambda ()
|
||||||
|
(set-source-properties! defmod
|
||||||
|
'((filename . "foo.scm")
|
||||||
|
(line . 0)
|
||||||
|
(column . 0)))
|
||||||
|
(compile `(begin
|
||||||
|
,defmod
|
||||||
|
(define (frob x)
|
||||||
|
(vlist-cons x vlist-null)))
|
||||||
|
#:env (make-fresh-user-module)
|
||||||
|
#:opts %opts-w-unused-module)))))
|
||||||
|
(and (= (length w) 1)
|
||||||
|
(string-prefix? "foo.scm:1:0" (car w))
|
||||||
|
(number? (string-contains (car w)
|
||||||
|
"unused module (ice-9 popen)")))))
|
||||||
|
|
||||||
|
(pass-if "definitely unused, use-modules"
|
||||||
|
(let* ((usemod '(use-modules (rnrs bytevectors)
|
||||||
|
(ice-9 q)))
|
||||||
|
(w (call-with-warnings
|
||||||
|
(lambda ()
|
||||||
|
(set-source-properties! usemod
|
||||||
|
'((filename . "bar.scm")
|
||||||
|
(line . 5)
|
||||||
|
(column . 0)))
|
||||||
|
(compile `(begin
|
||||||
|
,usemod
|
||||||
|
(define (square x)
|
||||||
|
(* x x)))
|
||||||
|
#:env (make-fresh-user-module)
|
||||||
|
#:opts %opts-w-unused-module)))))
|
||||||
|
(and (= (length w) 2)
|
||||||
|
(string-prefix? "bar.scm:6:0" (car w))
|
||||||
|
(number? (string-contains (car w)
|
||||||
|
"unused module (rnrs bytevectors)"))
|
||||||
|
(number? (string-contains (cadr w)
|
||||||
|
"unused module (ice-9 q)")))))
|
||||||
|
|
||||||
|
(pass-if "definitely unused, local binding shadows imported one"
|
||||||
|
(let ((w (call-with-warnings
|
||||||
|
(lambda ()
|
||||||
|
(compile `(begin
|
||||||
|
(define-module (whatever x y z)
|
||||||
|
#:use-module (ice-9 popen)
|
||||||
|
#:export (frob))
|
||||||
|
|
||||||
|
(define (open-input-pipe x)
|
||||||
|
;; Shadows the one from (ice-9 popen).
|
||||||
|
x)
|
||||||
|
(define (frob y)
|
||||||
|
(close-port (open-input-pipe y))))
|
||||||
|
#:env (make-fresh-user-module)
|
||||||
|
#:opts %opts-w-unused-module)))))
|
||||||
|
(and (= (length w) 1)
|
||||||
|
(number? (string-contains (car w)
|
||||||
|
"unused module (ice-9 popen)")))))
|
||||||
|
|
||||||
|
(pass-if-equal "(ice-9 match) is actually used"
|
||||||
|
'()
|
||||||
|
;; (ice-9 match) is used and the macro expansion of the 'match'
|
||||||
|
;; form refers to (@@ (ice-9 match) car) and the likes.
|
||||||
|
(call-with-warnings
|
||||||
|
(lambda ()
|
||||||
|
(compile '(begin
|
||||||
|
(use-modules (ice-9 match))
|
||||||
|
(define (proc lst)
|
||||||
|
(match lst
|
||||||
|
((a b c) (+ a (* b c))))))
|
||||||
|
#:env (make-fresh-user-module)
|
||||||
|
#:opts %opts-w-unused-module))))
|
||||||
|
|
||||||
|
(pass-if-equal "re-exporting is using"
|
||||||
|
'()
|
||||||
|
;; This module re-exports a binding from (ice-9 q), so (ice-9 q)
|
||||||
|
;; should be considered as used.
|
||||||
|
(call-with-warnings
|
||||||
|
(lambda ()
|
||||||
|
(compile '(begin
|
||||||
|
(define-module (this is an ice-9 q user)
|
||||||
|
#:use-module (ice-9 q)
|
||||||
|
#:re-export (make-q)
|
||||||
|
#:export (proc))
|
||||||
|
(define (proc a b)
|
||||||
|
(* a b)))
|
||||||
|
#:env (make-fresh-user-module)
|
||||||
|
#:opts %opts-w-unused-module))))
|
||||||
|
|
||||||
|
(pass-if "(srfi srfi-26) might be unused"
|
||||||
|
;; At the tree-il level, it is impossible to know whether (srfi
|
||||||
|
;; srfi-26) is actually use, because all we see is the output of
|
||||||
|
;; macro expansion, and in this case it doesn't capture any
|
||||||
|
;; binding from (srfi srfi-26).
|
||||||
|
(let* ((w (call-with-warnings
|
||||||
|
(lambda ()
|
||||||
|
(compile `(begin
|
||||||
|
(define-module (whatever)
|
||||||
|
#:use-module (srfi srfi-26)
|
||||||
|
#:export (square))
|
||||||
|
(define double
|
||||||
|
(cut * 2 <>)))
|
||||||
|
#:env (make-fresh-user-module)
|
||||||
|
#:opts %opts-w-unused-module)))))
|
||||||
|
(and (= (length w) 1)
|
||||||
|
(number? (string-contains (car w)
|
||||||
|
"possibly unused module (srfi srfi-26)")))))
|
||||||
|
|
||||||
|
(pass-if-equal "(ice-9 format) is actually used"
|
||||||
|
'()
|
||||||
|
;; The 'format' binding of (ice-9 format) takes precedence over
|
||||||
|
;; (@@ (guile) format), so (ice-9 format) must not be reported as
|
||||||
|
;; unused.
|
||||||
|
(call-with-warnings
|
||||||
|
(lambda ()
|
||||||
|
(compile '(begin
|
||||||
|
(define-module (whatever-else)
|
||||||
|
#:use-module (ice-9 format)
|
||||||
|
#:export (proc))
|
||||||
|
(define (proc lst)
|
||||||
|
(format #f "~{~a ~}~%" lst)))
|
||||||
|
#:env (make-fresh-user-module)
|
||||||
|
#:opts %opts-w-unused-module)))))
|
||||||
|
|
||||||
(with-test-prefix "shadowed-toplevel"
|
(with-test-prefix "shadowed-toplevel"
|
||||||
|
|
||||||
(pass-if "quiet"
|
(pass-if "quiet"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue