From fa27a2be2b95a039432cf8f1f60179b44f811798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Krassowski?= <5832902+krassowski@users.noreply.github.com> Date: Thu, 30 Jun 2022 21:45:46 +0100 Subject: [PATCH] Add `DiagnosticTag` support (#229) --- pylsp/plugins/flake8_lint.py | 44 ++++++++++++-------- pylsp/plugins/pycodestyle_lint.py | 7 +++- pylsp/plugins/pylint_lint.py | 68 +++++++++++++++++++++---------- test/plugins/test_flake8_lint.py | 1 + test/plugins/test_pylint_lint.py | 2 + 5 files changed, 81 insertions(+), 41 deletions(-) diff --git a/pylsp/plugins/flake8_lint.py b/pylsp/plugins/flake8_lint.py index 66aaaf36..0c5e09f6 100644 --- a/pylsp/plugins/flake8_lint.py +++ b/pylsp/plugins/flake8_lint.py @@ -13,6 +13,13 @@ log = logging.getLogger(__name__) FIX_IGNORES_RE = re.compile(r'([^a-zA-Z0-9_,]*;.*(\W+||$))') +UNNECESSITY_CODES = { + 'F401', # `module` imported but unused + 'F504', # % format unused named arguments + 'F522', # .format(...) unused named arguments + 'F523', # .format(...) unused positional arguments + 'F841' # local variable `name` is assigned to but never used +} @hookimpl @@ -176,24 +183,25 @@ def parse_stdout(document, stdout): severity = lsp.DiagnosticSeverity.Warning if code == "E999" or code[0] == "F": severity = lsp.DiagnosticSeverity.Error - diagnostics.append( - { - 'source': 'flake8', - 'code': code, - 'range': { - 'start': { - 'line': line, - 'character': character - }, - 'end': { - 'line': line, - # no way to determine the column - 'character': len(document.lines[line]) - } + diagnostic = { + 'source': 'flake8', + 'code': code, + 'range': { + 'start': { + 'line': line, + 'character': character }, - 'message': msg, - 'severity': severity, - } - ) + 'end': { + 'line': line, + # no way to determine the column + 'character': len(document.lines[line]) + } + }, + 'message': msg, + 'severity': severity, + } + if code in UNNECESSITY_CODES: + diagnostic['tags'] = [lsp.DiagnosticTag.Unnecessary] + diagnostics.append(diagnostic) return diagnostics diff --git a/pylsp/plugins/pycodestyle_lint.py b/pylsp/plugins/pycodestyle_lint.py index 99a6f074..30aeb67a 100644 --- a/pylsp/plugins/pycodestyle_lint.py +++ b/pylsp/plugins/pycodestyle_lint.py @@ -75,14 +75,17 @@ def error(self, line_number, offset, text, check): 'character': 100 if line_number > len(self.lines) else len(self.lines[line_number - 1]) }, } - self.diagnostics.append({ + diagnostic = { 'source': 'pycodestyle', 'range': err_range, 'message': text, 'code': code, # Are style errors really ever errors? 'severity': _get_severity(code) - }) + } + if code.startswith('W6'): + diagnostic['tags'] = [lsp.DiagnosticTag.Deprecated] + self.diagnostics.append(diagnostic) def _get_severity(code): diff --git a/pylsp/plugins/pylint_lint.py b/pylsp/plugins/pylint_lint.py index d974a2f8..f33cfcd8 100644 --- a/pylsp/plugins/pylint_lint.py +++ b/pylsp/plugins/pylint_lint.py @@ -28,6 +28,20 @@ # fix for a very specific upstream issue. # Related: https://github.com/PyCQA/pylint/issues/3518 os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = 'hide' +DEPRECATION_CODES = { + 'W0402', # Uses of a deprecated module %r + 'W1505', # Using deprecated method %s() + 'W1511', # Using deprecated argument %s of method %s() + 'W1512', # Using deprecated class %s of module %s + 'W1513', # Using deprecated decorator %s() +} +UNNECESSITY_CODES = { + 'W0611', # Unused import %s + 'W0612', # Unused variable %r + 'W0613', # Unused argument %r + 'W0614', # Unused import %s from wildcard import + 'W1304', # Unused-format-string-argument +} class PylintLinter: @@ -146,13 +160,22 @@ def lint(cls, document, is_saved, flags=''): elif diag['type'] == 'warning': severity = lsp.DiagnosticSeverity.Warning - diagnostics.append({ + code = diag['message-id'] + + diagnostic = { 'source': 'pylint', 'range': err_range, 'message': '[{}] {}'.format(diag['symbol'], diag['message']), 'severity': severity, - 'code': diag['message-id'] - }) + 'code': code + } + + if code in UNNECESSITY_CODES: + diagnostic['tags'] = [lsp.DiagnosticTag.Unnecessary] + if code in DEPRECATION_CODES: + diagnostic['tags'] = [lsp.DiagnosticTag.Deprecated] + + diagnostics.append(diagnostic) cls.last_diags[document.path] = diagnostics return diagnostics @@ -295,24 +318,27 @@ def _parse_pylint_stdio_result(document, stdout): 'W': lsp.DiagnosticSeverity.Warning, } severity = severity_map[code[0]] - diagnostics.append( - { - 'source': 'pylint', - 'code': code, - 'range': { - 'start': { - 'line': line, - 'character': character - }, - 'end': { - 'line': line, - # no way to determine the column - 'character': len(document.lines[line]) - 1 - } + diagnostic = { + 'source': 'pylint', + 'code': code, + 'range': { + 'start': { + 'line': line, + 'character': character }, - 'message': msg, - 'severity': severity, - } - ) + 'end': { + 'line': line, + # no way to determine the column + 'character': len(document.lines[line]) - 1 + } + }, + 'message': msg, + 'severity': severity, + } + if code in UNNECESSITY_CODES: + diagnostic['tags'] = [lsp.DiagnosticTag.Unnecessary] + if code in DEPRECATION_CODES: + diagnostic['tags'] = [lsp.DiagnosticTag.Deprecated] + diagnostics.append(diagnostic) return diagnostics diff --git a/test/plugins/test_flake8_lint.py b/test/plugins/test_flake8_lint.py index e9e7d216..6e162e8f 100644 --- a/test/plugins/test_flake8_lint.py +++ b/test/plugins/test_flake8_lint.py @@ -40,6 +40,7 @@ def test_flake8_unsaved(workspace): assert unused_var['range']['start'] == {'line': 5, 'character': 1} assert unused_var['range']['end'] == {'line': 5, 'character': 11} assert unused_var['severity'] == lsp.DiagnosticSeverity.Error + assert unused_var['tags'] == [lsp.DiagnosticTag.Unnecessary] def test_flake8_lint(workspace): diff --git a/test/plugins/test_pylint_lint.py b/test/plugins/test_pylint_lint.py index cbad9c3b..afd5c30d 100644 --- a/test/plugins/test_pylint_lint.py +++ b/test/plugins/test_pylint_lint.py @@ -51,6 +51,7 @@ def test_pylint(config, workspace): assert unused_import['range']['start'] == {'line': 0, 'character': 0} assert unused_import['severity'] == lsp.DiagnosticSeverity.Warning + assert unused_import['tags'] == [lsp.DiagnosticTag.Unnecessary] if IS_PY3: # test running pylint in stdin @@ -79,6 +80,7 @@ def test_syntax_error_pylint_py3(config, workspace): # Pylint doesn't give column numbers for invalid syntax. assert diag['range']['start'] == {'line': 0, 'character': 12} assert diag['severity'] == lsp.DiagnosticSeverity.Error + assert 'tags' not in diag # test running pylint in stdin config.plugin_settings('pylint')['executable'] = 'pylint'