Add warning for unused imports

This commit is contained in:
James Westman 2024-04-06 14:30:48 -05:00
parent 729939ad93
commit 6a078ee075
13 changed files with 53 additions and 5 deletions

View file

@ -125,7 +125,7 @@ class AstNode:
return self.parent.root
@property
def range(self):
def range(self) -> Range:
return Range(self.group.start, self.group.end, self.group.text)
def parent_by_type(self, type: T.Type[TType]) -> TType:

View file

@ -140,6 +140,10 @@ class DeprecatedWarning(CompileWarning):
pass
class UnusedWarning(CompileWarning):
pass
class UpgradeWarning(CompileWarning):
category = "upgrade"
color = Colors.PURPLE

View file

@ -35,6 +35,7 @@ from ..errors import (
CompileWarning,
DeprecatedWarning,
MultipleErrors,
UnusedWarning,
UpgradeWarning,
)
from ..gir import (

View file

@ -88,6 +88,17 @@ class Import(AstNode):
def namespace_exists(self):
gir.get_namespace(self.tokens["namespace"], self.tokens["version"])
@validate()
def unused(self):
if self.namespace not in self.root.used_imports:
raise UnusedWarning(
f"Unused import: {self.namespace}",
self.range,
actions=[
CodeAction("Remove import", "", self.range.with_trailing_newline)
],
)
@property
def gir_namespace(self):
try:

View file

@ -78,9 +78,10 @@ class TypeName(AstNode):
)
@property
def gir_ns(self):
def gir_ns(self) -> T.Optional[gir.Namespace]:
if not self.tokens["extern"]:
return self.root.gir.namespaces.get(self.tokens["namespace"] or "Gtk")
return None
@property
def gir_type(self) -> gir.GirType:

View file

@ -27,6 +27,7 @@ from .gtk_menu import Menu, menu
from .gtkbuilder_template import Template
from .imports import GtkDirective, Import
from .translation_domain import TranslationDomain
from .types import TypeName
class UI(AstNode):
@ -121,6 +122,22 @@ class UI(AstNode):
Range(pos, pos, self.group.text),
)
@cached_property
def used_imports(self) -> T.Optional[T.Set[str]]:
def _iter_recursive(node: AstNode):
yield node
for child in node.children:
if isinstance(child, AstNode):
yield from _iter_recursive(child)
result = set()
for node in _iter_recursive(self):
if isinstance(node, TypeName):
ns = node.gir_ns
if ns is not None:
result.add(ns.name)
return result
@context(ScopeCtx)
def scope_ctx(self) -> ScopeCtx:
return ScopeCtx(node=self)

View file

@ -475,6 +475,9 @@ class LanguageServer:
if isinstance(err, DeprecationWarning):
result["tags"] = [DiagnosticTag.Deprecated]
if isinstance(err, UnusedWarning):
result["tags"] = [DiagnosticTag.Unnecessary]
if len(err.references) > 0:
result["relatedInformation"] = [
{

View file

@ -127,6 +127,13 @@ class Range:
def text(self) -> str:
return self.original_text[self.start : self.end]
@property
def with_trailing_newline(self) -> "Range":
if len(self.original_text) > self.end and self.original_text[self.end] == "\n":
return Range(self.start, self.end + 1, self.original_text)
else:
return self
@staticmethod
def join(a: T.Optional["Range"], b: T.Optional["Range"]) -> T.Optional["Range"]:
if a is None:

View file

@ -1,5 +1,4 @@
using Gtk 4.0;
using Gio 2.0;
Dialog {
use-header-bar: 1;

View file

@ -1 +1 @@
4,1,6,Gtk.Dialog is deprecated
3,1,6,Gtk.Dialog is deprecated

View file

@ -0,0 +1,5 @@
using Gtk 4.0;
using GLib 2.0;
using Gio 2.0;
Gio.Cancellable {}

View file

@ -0,0 +1 @@
2,1,15,Unused import: GLib

View file

@ -1,5 +1,4 @@
using Gtk 4.0;
using Gio 2.0;
Gtk.Shortcut {
trigger: "Escape";