Skip to content

Commit

Permalink
Notify if Library or Resource cannot be resolved. Fixes #542
Browse files Browse the repository at this point in the history
  • Loading branch information
fabioz committed Jan 19, 2022
1 parent cd91022 commit f31410c
Show file tree
Hide file tree
Showing 12 changed files with 502 additions and 75 deletions.
71 changes: 66 additions & 5 deletions robotframework-ls/src/robotframework_ls/impl/code_analysis.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from typing import Dict, Set

from robocorp_ls_core.protocols import check_implements
from robocorp_ls_core.robotframework_log import get_logger
from robotframework_ls.impl.ast_utils import MAX_ERRORS
from typing import Dict, Set
from robotframework_ls.impl.protocols import IKeywordFound
from robotframework_ls.impl.protocols import IKeywordFound, IKeywordCollector


log = get_logger(__name__)

Expand Down Expand Up @@ -35,11 +38,13 @@ def contains_keyword(self, normalized_keyword_name):
return False


class _KeywordsCollector(object):
def __init__(self):
class _AnalysisKeywordsCollector(object):
def __init__(self, on_unresolved_library, on_unresolved_resource):
self._keywords_container = _KeywordContainer()
self._resource_name_to_keywords_container = {}
self._library_name_to_keywords_container = {}
self._on_unresolved_library = on_unresolved_library
self._on_unresolved_resource = on_unresolved_resource

def accepts(self, keyword_name):
return True
Expand Down Expand Up @@ -108,6 +113,33 @@ def contains_keyword(self, normalized_keyword_name):

return False

def on_unresolved_library(
self,
library_name: str,
lineno: int,
end_lineno: int,
col_offset: int,
end_col_offset: int,
):
self._on_unresolved_library(
library_name, lineno, end_lineno, col_offset, end_col_offset
)

def on_unresolved_resource(
self,
resource_name: str,
lineno: int,
end_lineno: int,
col_offset: int,
end_col_offset: int,
):
self._on_unresolved_resource(
resource_name, lineno, end_lineno, col_offset, end_col_offset
)

def __typecheckself__(self) -> None:
_: IKeywordCollector = check_implements(self)


def collect_analysis_errors(completion_context):
from robotframework_ls.impl import ast_utils
Expand All @@ -117,7 +149,36 @@ def collect_analysis_errors(completion_context):
from robotframework_ls.impl.text_utilities import contains_variable_text

errors = []
collector = _KeywordsCollector()

def on_unresolved_library(
library_name: str,
lineno: int,
end_lineno: int,
col_offset: int,
end_col_offset: int,
):
start = (lineno - 1, col_offset)
end = (end_lineno - 1, end_col_offset)
errors.append(
ast_utils.Error(f"Unresolved library: {library_name}", start, end)
)

def on_unresolved_resource(
library_name: str,
lineno: int,
end_lineno: int,
col_offset: int,
end_col_offset: int,
):
start = (lineno - 1, col_offset)
end = (end_lineno - 1, end_col_offset)
errors.append(
ast_utils.Error(f"Unresolved resource: {library_name}", start, end)
)

collector = _AnalysisKeywordsCollector(
on_unresolved_library, on_unresolved_resource
)
collect_keywords(completion_context, collector)

ast = completion_context.get_ast()
Expand Down
76 changes: 72 additions & 4 deletions robotframework-ls/src/robotframework_ls/impl/collect_keywords.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ def _collect_current_doc_keywords(
_collect_completions_from_ast(ast, completion_context, collector)


_LibInfo = namedtuple("_LibInfo", "name, alias, builtin, args")
_LibInfo = namedtuple("_LibInfo", "name, alias, builtin, args, node")


def _collect_libraries_keywords(
Expand All @@ -269,11 +269,12 @@ def _collect_libraries_keywords(
# Note: using a dict(_LibInfo:bool) where only the keys are meaningful
# because we want to keep the order and sets aren't ordered.
library_infos = {}
for name, alias, args in (
for name, alias, args, node in (
(
library.name,
library.alias,
ast_utils.get_library_arguments_serialized(library),
library,
)
for library in libraries
):
Expand All @@ -283,11 +284,12 @@ def _collect_libraries_keywords(
alias,
False,
args,
node,
)

library_infos[lib_info] = True

library_infos[_LibInfo(BUILTIN_LIB, None, True, None)] = True
library_infos[_LibInfo(BUILTIN_LIB, None, True, None, None)] = True
libspec_manager = completion_context.workspace.libspec_manager

for library_info in library_infos:
Expand Down Expand Up @@ -325,12 +327,55 @@ def _collect_libraries_keywords(
library_alias=library_info.alias,
)
)
else:
from robot.api import Token

node = library_info.node
node_name_tok = node.get_token(Token.NAME)
if node_name_tok is not None:
collector.on_unresolved_library(
node.name,
node_name_tok.lineno,
node_name_tok.lineno,
node_name_tok.col_offset,
node_name_tok.end_col_offset,
)
else:
collector.on_unresolved_library(
library_info.name,
node.lineno,
node.end_lineno,
node.col_offset,
node.end_col_offset,
)


def _collect_resource_imports_keywords(
completion_context: ICompletionContext, collector: IKeywordCollector
):
for resource_doc in completion_context.get_resource_imports_as_docs():

for node, resource_doc in completion_context.get_resource_imports_as_docs():
if resource_doc is None:
from robot.api import Token

node_name_tok = node.get_token(Token.NAME)
if node_name_tok is not None:
collector.on_unresolved_resource(
node.name,
node_name_tok.lineno,
node_name_tok.lineno,
node_name_tok.col_offset,
node_name_tok.end_col_offset,
)
else:
collector.on_unresolved_resource(
node.name,
node.lineno,
node.end_lineno,
node.col_offset,
node.end_col_offset,
)
continue
new_ctx = completion_context.create_copy(resource_doc)
_collect_following_imports(new_ctx, collector)

Expand Down Expand Up @@ -362,6 +407,29 @@ def on_keyword(self, keyword_found: IKeywordFound):
self.keyword_name_to_keyword_found[keyword_found.keyword_name] = lst = []
lst.append(keyword_found)

def on_unresolved_library(
self,
library_name: str,
lineno: int,
end_lineno: int,
col_offset: int,
end_col_offset: int,
):
pass

def on_unresolved_resource(
self,
resource_name: str,
lineno: int,
end_lineno: int,
col_offset: int,
end_col_offset: int,
):
pass

def __typecheckself__(self) -> None:
_: IKeywordCollector = check_implements(self)


def collect_keyword_name_to_keyword_found(
completion_context: ICompletionContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
ILibraryImportNode,
KeywordUsageInfo,
CompletionType,
INode,
IResourceImportNode,
)
import sys

Expand Down Expand Up @@ -450,15 +452,16 @@ def get_variable_import_as_doc(self, variables_import) -> Optional[IRobotDocumen
return self.get_resource_import_as_doc(variables_import)

@instance_cache
def get_resource_imports_as_docs(self) -> Tuple[IRobotDocument, ...]:
ret: List[IRobotDocument] = []
def get_resource_imports_as_docs(
self,
) -> Tuple[Tuple[IResourceImportNode, Optional[IRobotDocument]], ...]:
ret: List[Tuple[IResourceImportNode, Optional[IRobotDocument]]] = []

# Get keywords from resources
resource_imports = self.get_resource_imports()
for resource_import in resource_imports:
resource_doc = self.get_resource_import_as_doc(resource_import)
if resource_doc is not None:
ret.append(resource_doc)
ret.append((resource_import, resource_doc))

return tuple(ret)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,12 @@ def complete(completion_context: ICompletionContext):
except ValueError:
return []
variables = _get_variables(completion_context)
for resource_doc in completion_context.get_resource_imports_as_docs():
for (
_resource_node,
resource_doc,
) in completion_context.get_resource_imports_as_docs():
if resource_doc is None:
continue
new_ctx = completion_context.create_copy(resource_doc)
variables += _get_variables(new_ctx)
dict_name = _get_dict_name(value)
Expand Down
40 changes: 30 additions & 10 deletions robotframework-ls/src/robotframework_ls/impl/find_definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
IDefinition,
TokenInfo,
IKeywordDefinition,
IKeywordCollector,
IVariablesCollector,
)
from robocorp_ls_core.protocols import check_implements
from typing import Optional, Sequence
Expand Down Expand Up @@ -138,15 +140,7 @@ def __typecheckself__(self) -> None:
_: IDefinition = check_implements(self)


class IDefinitionsCollector(object):
def accepts(self, keyword_name):
pass

def on_keyword(self, keyword_found):
pass


class _FindDefinitionKeywordCollector(IDefinitionsCollector):
class _FindDefinitionKeywordCollector(object):
def __init__(self, match_name):
from robotframework_ls.impl.string_matcher import RobotStringMatcher
from robotframework_ls.impl.string_matcher import (
Expand Down Expand Up @@ -174,8 +168,31 @@ def on_keyword(self, keyword_found):
self.matches.append(definition)
return

def on_unresolved_library(
self,
library_name: str,
lineno: int,
end_lineno: int,
col_offset: int,
end_col_offset: int,
):
pass

class _FindDefinitionVariablesCollector(IDefinitionsCollector):
def on_unresolved_resource(
self,
resource_name: str,
lineno: int,
end_lineno: int,
col_offset: int,
end_col_offset: int,
):
pass

def __typecheckself__(self) -> None:
_: IKeywordCollector = check_implements(self)


class _FindDefinitionVariablesCollector(object):
def __init__(self, sel, token, robot_string_matcher):
self.matches = []
self.sel = sel
Expand All @@ -189,6 +206,9 @@ def on_variable(self, variable_found):
definition = _DefinitionFromVariable(variable_found)
self.matches.append(definition)

def __typecheckself__(self) -> None:
_: IVariablesCollector = check_implements(self)


def find_keyword_definition(
completion_context: ICompletionContext, token_info: TokenInfo
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
from robocorp_ls_core.robotframework_log import get_logger
from robotframework_ls.impl.protocols import ICompletionContext, IKeywordFound
from typing import List

from robocorp_ls_core.protocols import check_implements
from robocorp_ls_core.robotframework_log import get_logger
from robotframework_ls.impl.protocols import (
ICompletionContext,
IKeywordFound,
IKeywordCollector,
)


log = get_logger(__name__)


Expand Down Expand Up @@ -111,6 +118,29 @@ def on_keyword(self, keyword_found):

self.completion_items.append(item)

def on_unresolved_library(
self,
library_name: str,
lineno: int,
end_lineno: int,
col_offset: int,
end_col_offset: int,
):
pass

def on_unresolved_resource(
self,
library_name: str,
lineno: int,
end_lineno: int,
col_offset: int,
end_col_offset: int,
):
pass

def __typecheckself__(self) -> None:
_: IKeywordCollector = check_implements(self)


def complete(completion_context: ICompletionContext) -> List[dict]:
from robotframework_ls.impl.collect_keywords import collect_keywords
Expand Down
Loading

0 comments on commit f31410c

Please sign in to comment.