sbcl - 如何消除“未定义变量”警告?

mag*_*age 5 sbcl common-lisp suppress-warnings

我不知道如何做到这一点sb-ext:muffle-conditions。我想做这样的事情:

(declaim #+sbcl(sb-ext:muffle-conditions sb-kernel:redefinition-warning))
Run Code Online (Sandbox Code Playgroud)

当然,除了我想消除“未定义的变量”警告而不是重新定义。

如果有人知道这是什么参数或有文档/各种选项的链接sb-ext:muffle-conditions,请分享:)谢谢

Jos*_*lor 4

我不确定您是否能够专门消除这种类型的警告,至少通过类名。通过跟踪warn,我们可以了解 SBCL 正在做什么。例如,看看重新定义的情况下会发生什么:

* (trace warn)

(WARN)
* (defun foo () nil)

FOO
* (defun foo () nil)
  0: (WARN SB-KERNEL:REDEFINITION-WITH-DEFUN :NAME FOO :NEW-FUNCTION
           #<FUNCTION FOO {10041FA989}> :NEW-LOCATION
           #S(SB-C:DEFINITION-SOURCE-LOCATION
              :NAMESTRING NIL
              :TOPLEVEL-FORM-NUMBER NIL
              :PLIST NIL))
STYLE-WARNING: redefining COMMON-LISP-USER::FOO in DEFUN
  0: WARN returned NIL
FOO
Run Code Online (Sandbox Code Playgroud)

warn使用类sb-kernel:redefinition-with-defun和参数作为类指示符进行调用,因此发出的警告具有某种特定的类类型。能够基于特定的类别类型进行消声使得消声变得更容易。

现在,看看未定义变量的情况下会发生什么:

* (defun foo2 () x)
  0: (WARN "undefined ~(~A~): ~S" :VARIABLE X)

; in: DEFUN FOO2
;     (BLOCK FOO2 X)
; 
; caught WARNING:
;   undefined variable: X
  0: WARN returned NIL
; 
; compilation unit finished
;   Undefined variable:
;     X
;   caught 1 WARNING condition
FOO2
Run Code Online (Sandbox Code Playgroud)

warn正在使用格式字符串和一些参数进行调用,因此发出的警告只是一个simple-warning. 现在,你仍然可以采取一些措施来消除它,但它有点复杂。

根据SBCL手册部分,3.1.1 Controlling Verbositysb-ext:muffle-conditions只是使用muffle-warning重新启动。因为未定义的变量警告只是一个simple-warning,并且我们可能不想隐藏所有 simple-warnings ,所以我们需要有点偷偷摸摸地使用由处理程序绑定指定的处理程序来检查条件。warn由于我们已经看到了被调用的参数,因此我们可以非常具体地了解我们捕获的内容。我们可以通过以下方式识别这些警告undefined-variable-warning-p

(defun undefined-variable-warning-p (w)
  (let ((control (simple-condition-format-control w))
        (arguments (simple-condition-format-arguments w)))
    (and (= 2 (length arguments))
         (eq :variable (first arguments))
         (string= control "undefined ~(~A~): ~S"))))
Run Code Online (Sandbox Code Playgroud)

现在我们可以将编译表单包装在适当的handler-bind. 例如,让我们看看(compile nil (lambda () x))有和没有处理程序:

CL-USER> (compile nil '(lambda () x))
; 
; caught WARNING:
;   undefined variable: X
; 
; compilation unit finished
;   Undefined variable:
;     X
;   caught 1 WARNING condition
#<FUNCTION (LAMBDA ()) {1003AA4F89}>
T
T

CL-USER> (handler-bind
             ((simple-warning 
               #'(lambda (w) 
                   (when (undefined-variable-warning-p w)
                     (invoke-restart 'muffle-warning)))))
           (compile nil '(lambda () x)))
#<FUNCTION (LAMBDA ()) {1003B737E9}>
NIL
NIL
Run Code Online (Sandbox Code Playgroud)

我们已经成功编译了该函数并消除了未定义变量的警告。但是,请注意,您不能简单地将defuns 包裹在其中。例如,

CL-USER> (handler-bind
             ((simple-warning 
               #'(lambda (w) 
                   (when (undefined-variable-warning-p w)
                     (invoke-restart 'muffle-warning)))))
           (defun some-function () x))

; in: DEFUN SOME-FUNCTION
;     (DEFUN SOME-FUNCTION () X)
; --> PROGN EVAL-WHEN SB-IMPL::%DEFUN SB-INT:NAMED-LAMBDA FUNCTION 
; ==>
;   (BLOCK SOME-FUNCTION X)
; 
; caught WARNING:
;   undefined variable: X
; 
; compilation unit finished
;   Undefined variable:
;     X
;   caught 1 WARNING condition
SOME-FUNCTION
Run Code Online (Sandbox Code Playgroud)

但是,如果您eval也一样defun(但我并不是说您应该这样做),则警告将被抑制:

CL-USER> (handler-bind
             ((simple-warning 
               #'(lambda (w) 
                   (when (undefined-variable-warning-p w)
                     (invoke-restart 'muffle-warning)))))
           (eval '(defun some-other-function () x)))
SOME-OTHER-FUNCTION
Run Code Online (Sandbox Code Playgroud)

我不确定这是为什么,但我希望有人能够在评论中详细说明。我怀疑defun这来自 REPL 上的 SBCL 编译表单,这意味着在整个表单运行之前编译主体,因此编译是在处理程序就位之前发生的。