[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/eglot 37706af 21/26: Warn about suspicious interface us
From: |
Jo�o T�vora |
Subject: |
[elpa] externals/eglot 37706af 21/26: Warn about suspicious interface usage at compile-time |
Date: |
Sun, 9 Dec 2018 19:11:29 -0500 (EST) |
branch: externals/eglot
commit 37706af9b7579bdb7ce75e36008d217252ccf51d
Author: João Távora <address@hidden>
Commit: João Távora <address@hidden>
Warn about suspicious interface usage at compile-time
For fun, set eglot-strict-mode to '(disallow-non-standard-keys
enforce-required-keys enforce-optional-keys) when compiling, or just
use flymake-mode in eglot.el.
* eglot.el (eglot--lsp-interface-alist): Use in compile-time.
Order alphabetically. Fix a few bugs.
(eglot-strict-mode): Disallow non-standard-keys when compiling.
Update docstring.
(eglot--keywordize-vars, eglot--check-interface): New
compile-time-helpers.
(eglot--dbind, eglot--dcase): Use new helpers.
---
eglot.el | 189 +++++++++++++++++++++++++++++++++++++++++----------------------
1 file changed, 122 insertions(+), 67 deletions(-)
diff --git a/eglot.el b/eglot.el
index ba61de9..0286f75 100644
--- a/eglot.el
+++ b/eglot.el
@@ -203,40 +203,41 @@ let the buffer grow forever."
;;; Message verification helpers
;;;
-(defvar eglot--lsp-interface-alist
- `((CodeAction (:title) (:kind :diagnostics :edit :command))
- (Command (:title :command) (:arguments))
- (FileSystemWatcher (:globPattern) (:kind))
- (Registration (:id :method) (:registerOptions))
- (Hover (:contents) (:range))
- (SymbolInformation
- (:name :kind :location)
- (:deprecated :containerName))
- (Position (:line :character))
- (Range (:start :end))
- (Location (:uri :range))
- (Diagnostic (:range :message)
- (:severity :code :source :relatedInformation))
- (TextEdit (:range :newText))
- (TextDocumentEdit (:textDocument :edits) ())
- (VersionedTextDocumentIdentifier (:uri :version) ())
- (WorkspaceEdit () (:changes :documentChanges))
- (MarkupContent (:kind :value))
- (InitializeResult (:capabilities))
- (ShowMessageParams (:type :message))
- (ShowMessageRequestParams (:type :message) (:actions))
- (LogMessageParams (:type :message))
- (Registration (:id :method) (:registerOptions))
- (CompletionItem
- (:label )
- (:kind :detail :documentation :deprecated :preselect :sortText :filterText
- :insertText :insertTextFormat :textEdit :additionalTextEdits?
- :commitCharacters :command :data))
- (SignatureHelp (:signatures) (:activeSignature :activeParameter))
- (SignatureInformation (:label) (:documentation :parameters))
- (ParameterInformation (:label) (:documentation))
- (DocumentHighlight) (:range) (:kind))
- "Alist (INTERFACE-NAME . INTERFACE) of known external LSP interfaces.
+(eval-and-compile
+ (defvar eglot--lsp-interface-alist
+ `(
+ (CodeAction (:title) (:kind :diagnostics :edit :command))
+ (Command (:title :command) (:arguments))
+ (CompletionItem (:label)
+ (:kind :detail :documentation :deprecated :preselect
+ :sortText :filterText :insertText
:insertTextFormat
+ :textEdit :additionalTextEdits :commitCharacters
+ :command :data))
+ (Diagnostic (:range :message) (:severity :code :source
:relatedInformation))
+ (DocumentHighlight (:range) (:kind))
+ (FileSystemWatcher (:globPattern) (:kind))
+ (Hover (:contents) (:range))
+ (InitializeResult (:capabilities))
+ (Location (:uri :range))
+ (LogMessageParams (:type :message))
+ (MarkupContent (:kind :value))
+ (ParameterInformation (:label) (:documentation))
+ (Position (:line :character))
+ (Range (:start :end))
+ (Registration (:id :method) (:registerOptions))
+ (Registration (:id :method) (:registerOptions))
+ (ResponseError (:code :message) (:data))
+ (ShowMessageParams (:type :message))
+ (ShowMessageRequestParams (:type :message) (:actions))
+ (SignatureHelp (:signatures) (:activeSignature :activeParameter))
+ (SignatureInformation (:label) (:documentation :parameters))
+ (SymbolInformation (:name :kind :location) (:deprecated :containerName))
+ (TextDocumentEdit (:textDocument :edits) ())
+ (TextEdit (:range :newText))
+ (VersionedTextDocumentIdentifier (:uri :version) ())
+ (WorkspaceEdit () (:changes :documentChanges))
+ )
+ "Alist (INTERFACE-NAME . INTERFACE) of known external LSP interfaces.
INTERFACE-NAME is a symbol designated by the spec as
\"interface\". INTERFACE is a list (REQUIRED OPTIONAL) where
@@ -246,23 +247,38 @@ message adhering to that interface.
Here's what an element of this alist might look like:
- (CreateFile . ((:kind :uri) (:options)))")
+ (CreateFile . ((:kind :uri) (:options)))"))
-(defvar eglot-strict-mode '()
- "How strictly Eglot vetoes LSP messages from server.
+(eval-and-compile
+ (defvar eglot-strict-mode (if load-file-name '()
+ '(disallow-non-standard-keys
+ ;; Uncomment these two for fun at
+ ;; compile-time or with flymake-mode.
+ ;;
+ ;; enforce-required-keys
+ ;; enforce-optional-keys
+ ))
+ "How strictly to check LSP interfaces at compile- and run-time.
Value is a list of symbols:
-If a list containing the symbol `disallow-non-standard-keys', an
-error is raised if any non-standard fields are sent by the
-server.
+If the symbol `disallow-non-standard-keys' is present, an error
+is raised if any extraneous fields are sent by the server. At
+compile-time, a warning is raised if a destructuring spec
+includes such a field.
-If the list containing the symbol `enforce-required-keys', an error
-is raised if any required fields are missing from the message.
+If the symbol `enforce-required-keys' is present, an error is
+raised if any required fields are missing from the message sent
+from the server. At compile-time, a warning is raised if a
+destructuring spec doesn't use such a field.
+
+If the symbol `enforce-optional-keys' is present, nothing special
+happens at run-time. At compile-time, a warning is raised if a
+destructuring spec doesn't use all optional fields.
If the list is empty, any non-standard fields sent by the server
and missing required fields are accepted (which may or may not
-cause problems in Eglot's functioning later on).")
+cause problems in Eglot's functioning later on)."))
(defun eglot--plist-keys (plist)
(cl-loop for (k _v) on plist by #'cddr collect k))
@@ -280,6 +296,45 @@ cause problems in Eglot's functioning later on).")
(eglot--error "A `%s' mustn't have %s" (car interface) excess))
(funcall fn))
+(eval-and-compile
+ (defun eglot--keywordize-vars (vars)
+ (mapcar (lambda (var) (intern (format ":%s" var))) vars))
+
+ (defun eglot--check-interface (interface-name vars)
+ (let ((interface
+ (assoc interface-name eglot--lsp-interface-alist)))
+ (cond (interface
+ (let ((too-many
+ (and
+ (memq 'disallow-non-standard-keys eglot-strict-mode)
+ (cl-set-difference
+ (eglot--keywordize-vars vars)
+ (append (car (cdr interface))
+ (cadr (cdr interface))))))
+ (ignored-required
+ (and
+ (memq 'enforce-required-keys eglot-strict-mode)
+ (cl-set-difference
+ (car (cdr interface))
+ (eglot--keywordize-vars vars))))
+ (missing-out
+ (and
+ (memq 'enforce-optional-keys eglot-strict-mode)
+ (cl-set-difference
+ (cadr (cdr interface))
+ (eglot--keywordize-vars vars)))))
+ (when too-many (byte-compile-warn
+ "Destructuring for %s has extraneous %s"
+ interface-name too-many))
+ (when ignored-required (byte-compile-warn
+ "Destructuring for %s ignores required
%s"
+ interface-name ignored-required))
+ (when missing-out (byte-compile-warn
+ "Destructuring for %s is missing out on %s"
+ interface-name missing-out))))
+ (t
+ (byte-compile-warn "Unknown LSP interface %s" interface-name))))))
+
(cl-defmacro eglot--dbind (vars object &body body)
"Destructure OBJECT of binding VARS in BODY.
VARS is ([(INTERFACE)] SYMS...)
@@ -290,8 +345,7 @@ Honour `eglot-strict-mode'."
(object-once (make-symbol "object-once"))
(fn-once (make-symbol "fn-once")))
(cond (interface-name
- ;; address@hidden: maybe we check some things at compile
- ;; time and use `byte-compiler-warn' here
+ (eglot--check-interface interface-name vars)
`(let ((,object-once ,object))
(cl-destructuring-bind (&key ,@vars &allow-other-keys)
,object-once
(eglot--call-with-interface (assoc ',interface-name
@@ -325,31 +379,32 @@ treated as in `eglot-dbind'."
(cond
,@(cl-loop
for (vars . body) in clauses
- for vars-as-keywords = (mapcar (lambda (var)
- (intern (format ":%s" var)))
- vars)
+ for vars-as-keywords = (eglot--keywordize-vars vars)
for interface-name = (if (consp (car vars))
(car (pop vars)))
for condition =
- (if interface-name
- ;; In this mode, we assume `eglot-strict-mode' is fully
- ;; on, otherwise we can't disambiguate between certain
- ;; types.
- `(let* ((interface
- (or (assoc ',interface-name eglot--lsp-interface-alist)
- (eglot--error "Unknown interface %s")))
- (object-keys (eglot--plist-keys ,obj-once))
- (required-keys (car (cdr interface))))
- (and (null (cl-set-difference required-keys object-keys))
- (null (cl-set-difference
- (cl-set-difference object-keys required-keys)
- (cadr (cdr interface))))))
- ;; In this interface-less mode we don't check
- ;; `eglot-strict-mode' at all: just check that the object
- ;; has all the keys the user wants to destructure.
- `(null (cl-set-difference
- ',vars-as-keywords
- (eglot--plist-keys ,obj-once))))
+ (cond (interface-name
+ (eglot--check-interface interface-name vars)
+ ;; In this mode, in runtime, we assume
+ ;; `eglot-strict-mode' is fully on, otherwise we
+ ;; can't disambiguate between certain types.
+ `(let* ((interface
+ (or (assoc ',interface-name
eglot--lsp-interface-alist)
+ (eglot--error "Unknown LSP interface %s"
+ ',interface-name)))
+ (object-keys (eglot--plist-keys ,obj-once))
+ (required-keys (car (cdr interface))))
+ (and (null (cl-set-difference required-keys object-keys))
+ (null (cl-set-difference
+ (cl-set-difference object-keys required-keys)
+ (cadr (cdr interface)))))))
+ (t
+ ;; In this interface-less mode we don't check
+ ;; `eglot-strict-mode' at all: just check that the object
+ ;; has all the keys the user wants to destructure.
+ `(null (cl-set-difference
+ ',vars-as-keywords
+ (eglot--plist-keys ,obj-once)))))
collect `(,condition
(cl-destructuring-bind (&key ,@vars &allow-other-keys)
,obj-once
- [elpa] externals/eglot f63bedb 20/26: Fix #144: Use eglot--dbind and eglot--lambda throughout, (continued)
- [elpa] externals/eglot f63bedb 20/26: Fix #144: Use eglot--dbind and eglot--lambda throughout, Jo�o T�vora, 2018/12/09
- [elpa] externals/eglot 3922cf3 01/26: Per #144, #156: control strictness towards incoming LSP messages, Jo�o T�vora, 2018/12/09
- [elpa] externals/eglot 8140be5 07/26: Touch up last commit, Jo�o T�vora, 2018/12/09
- [elpa] externals/eglot 95ef9e1 08/26: Robustify tests against (M)ELPA eglot installations, Jo�o T�vora, 2018/12/09
- [elpa] externals/eglot 38da3d3 15/26: Fix #159: Properly clear old diagnostics when making new ones, Jo�o T�vora, 2018/12/09
- [elpa] externals/eglot 444a8c3 16/26: Per #173: robustify previous fix against non-standard insertion bindings, Jo�o T�vora, 2018/12/09
- [elpa] externals/eglot 1d42be4 13/26: Close #173: support completionContext to help servers like ccls, Jo�o T�vora, 2018/12/09
- [elpa] externals/eglot e2200ce 09/26: Simplify interface of eglot--dbind macro, Jo�o T�vora, 2018/12/09
- [elpa] externals/eglot c1848c3 23/26: Handle array params to server notification or requests, Jo�o T�vora, 2018/12/09
- [elpa] externals/eglot 23accee 26/26: * eglot.el (Version): Bump to 1.3, Jo�o T�vora, 2018/12/09
- [elpa] externals/eglot 37706af 21/26: Warn about suspicious interface usage at compile-time,
Jo�o T�vora <=
- [elpa] externals/eglot 6de3d9c 10/26: Per #171, #156: Introduce eglot--dcase, Jo�o T�vora, 2018/12/09
- [elpa] externals/eglot a46f003 17/26: Fix #164: handle CodeAction/Command polymorphism with eglot--dcase, Jo�o T�vora, 2018/12/09
- [elpa] externals/eglot 6b0b1b7 24/26: Be lenient by default to unknown methods or notifications, Jo�o T�vora, 2018/12/09
- [elpa] externals/eglot 973cd81 25/26: Close #180: Add preamble to comparison to lsp-mode.el, Jo�o T�vora, 2018/12/09
- [elpa] externals/eglot a1f2033 12/26: Fix #116, #150: don't break in indirect buffers, Jo�o T�vora, 2018/12/09