diff --git a/robotframework-ls/src/robotframework_ls/impl/code_analysis.py b/robotframework-ls/src/robotframework_ls/impl/code_analysis.py index 93a3b73b6a..336ac94af6 100644 --- a/robotframework-ls/src/robotframework_ls/impl/code_analysis.py +++ b/robotframework-ls/src/robotframework_ls/impl/code_analysis.py @@ -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__) @@ -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 @@ -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 @@ -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() diff --git a/robotframework-ls/src/robotframework_ls/impl/collect_keywords.py b/robotframework-ls/src/robotframework_ls/impl/collect_keywords.py index d328057582..af208141d2 100644 --- a/robotframework-ls/src/robotframework_ls/impl/collect_keywords.py +++ b/robotframework-ls/src/robotframework_ls/impl/collect_keywords.py @@ -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( @@ -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 ): @@ -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: @@ -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) @@ -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, diff --git a/robotframework-ls/src/robotframework_ls/impl/completion_context.py b/robotframework-ls/src/robotframework_ls/impl/completion_context.py index 7ee93637a9..405da265eb 100644 --- a/robotframework-ls/src/robotframework_ls/impl/completion_context.py +++ b/robotframework-ls/src/robotframework_ls/impl/completion_context.py @@ -13,6 +13,8 @@ ILibraryImportNode, KeywordUsageInfo, CompletionType, + INode, + IResourceImportNode, ) import sys @@ -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) diff --git a/robotframework-ls/src/robotframework_ls/impl/dictionary_completions.py b/robotframework-ls/src/robotframework_ls/impl/dictionary_completions.py index ed9769f787..b76e282cb4 100644 --- a/robotframework-ls/src/robotframework_ls/impl/dictionary_completions.py +++ b/robotframework-ls/src/robotframework_ls/impl/dictionary_completions.py @@ -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) diff --git a/robotframework-ls/src/robotframework_ls/impl/find_definition.py b/robotframework-ls/src/robotframework_ls/impl/find_definition.py index 2520cab248..d977f88600 100644 --- a/robotframework-ls/src/robotframework_ls/impl/find_definition.py +++ b/robotframework-ls/src/robotframework_ls/impl/find_definition.py @@ -4,6 +4,8 @@ IDefinition, TokenInfo, IKeywordDefinition, + IKeywordCollector, + IVariablesCollector, ) from robocorp_ls_core.protocols import check_implements from typing import Optional, Sequence @@ -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 ( @@ -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 @@ -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 diff --git a/robotframework-ls/src/robotframework_ls/impl/keyword_completions.py b/robotframework-ls/src/robotframework_ls/impl/keyword_completions.py index ad5e00540f..7675eb1bf7 100644 --- a/robotframework-ls/src/robotframework_ls/impl/keyword_completions.py +++ b/robotframework-ls/src/robotframework_ls/impl/keyword_completions.py @@ -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__) @@ -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 diff --git a/robotframework-ls/src/robotframework_ls/impl/protocols.py b/robotframework-ls/src/robotframework_ls/impl/protocols.py index 168c8ba311..6001dcc5a8 100644 --- a/robotframework-ls/src/robotframework_ls/impl/protocols.py +++ b/robotframework-ls/src/robotframework_ls/impl/protocols.py @@ -1,5 +1,14 @@ import sys -from typing import TypeVar, Any, Optional, List, Sequence, Tuple, Iterable, Generic +from typing import ( + TypeVar, + Any, + Optional, + List, + Sequence, + Tuple, + Iterable, + Generic, +) from robocorp_ls_core.protocols import ( Sentinel, IMonitor, @@ -35,6 +44,24 @@ class INode(Protocol): class ILibraryImportNode(INode, Protocol): name: str alias: Optional[str] + lineno: int + end_lineno: int + col_offset: int + end_col_offset: int + + def get_token(self, name: str) -> Any: + pass + + +class IResourceImportNode(INode, Protocol): + name: str + lineno: int + end_lineno: int + col_offset: int + end_col_offset: int + + def get_token(self, name: str) -> Any: + pass class NodeInfo(Generic[Y]): @@ -216,6 +243,26 @@ def on_keyword(self, keyword_found: IKeywordFound): :param IKeywordFound 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 + class IDefinition(Protocol): @@ -362,7 +409,9 @@ def get_variable_import_as_doc(self, variables_import) -> Optional[IRobotDocumen def get_current_keyword_definition(self) -> Optional[IKeywordDefinition]: pass - def get_resource_imports_as_docs(self) -> Tuple[IRobotDocument, ...]: + def get_resource_imports_as_docs( + self, + ) -> Tuple[Tuple[IResourceImportNode, Optional[IRobotDocument]], ...]: pass def get_variable_imports_as_docs(self) -> Tuple[IRobotDocument, ...]: @@ -378,3 +427,50 @@ def get_current_keyword_definition_and_usage_info( self, ) -> Optional[Tuple[IKeywordDefinition, KeywordUsageInfo]]: pass + + +class IVariableFound(Protocol): + """ + :ivar variable_name: + :ivar variable_value: + :ivar completion_context: + This may be a new completion context, created when a new document is + being analyzed (the variable was created for that completion context). + :ivar source: + Source where the variable was found. + :ivar lineno: + Line where it was found (0-based). + """ + + variable_name: str = "" + variable_value: str = "" + completion_context: Optional[ICompletionContext] = None + + @property + def source(self) -> str: + pass + + # Note: line/offsets 0-based. + @property + def lineno(self) -> int: + pass + + @property + def end_lineno(self) -> int: + pass + + @property + def col_offset(self) -> int: + pass + + @property + def end_col_offset(self) -> int: + pass + + +class IVariablesCollector(Protocol): + def accepts(self, variable_name: str) -> bool: + pass + + def on_variable(self, variable_found: IVariableFound): + pass diff --git a/robotframework-ls/src/robotframework_ls/impl/robot_workspace.py b/robotframework-ls/src/robotframework_ls/impl/robot_workspace.py index 45d769b779..1033a72c39 100644 --- a/robotframework-ls/src/robotframework_ls/impl/robot_workspace.py +++ b/robotframework-ls/src/robotframework_ls/impl/robot_workspace.py @@ -25,8 +25,6 @@ import weakref import threading from robocorp_ls_core.lsp import TextDocumentContentChangeEvent, TextDocumentItem -import os -from robocorp_ls_core.uris import normalize_drive from robocorp_ls_core import uris log = get_logger(__name__) @@ -292,13 +290,16 @@ def _on_thread(self) -> None: old_cached = self._cached new_cached = {} + test_info_lst: List[ITestInfoFromSymbolsCacheTypedDict] for uri, symbols_cache in self.iter_uri_and_symbols_cache(): if symbols_cache is None: test_info_lst = [] else: - test_info_lst = symbols_cache.get_test_info() - if test_info_lst is None: + lst = symbols_cache.get_test_info() + if lst is None: test_info_lst = [] + else: + test_info_lst = lst if uri: test_info_for_uri: ITestInfoFromUriTypedDict = { @@ -334,12 +335,14 @@ def _on_thread(self) -> None: if symbols_cache is None: test_info_lst = [] else: - test_info_lst = symbols_cache.get_test_info() - if test_info_lst is None: + lst = symbols_cache.get_test_info() + if lst is None: test_info_lst = [] + else: + test_info_lst = lst if uri: - test_info_for_uri: ITestInfoFromUriTypedDict = { + test_info_for_uri = { "uri": uri, "testInfo": test_info_lst, } diff --git a/robotframework-ls/src/robotframework_ls/impl/variable_completions.py b/robotframework-ls/src/robotframework_ls/impl/variable_completions.py index 0e257ad728..0459e8941d 100644 --- a/robotframework-ls/src/robotframework_ls/impl/variable_completions.py +++ b/robotframework-ls/src/robotframework_ls/impl/variable_completions.py @@ -1,36 +1,17 @@ from robocorp_ls_core.cache import instance_cache -from robotframework_ls.impl.protocols import ICompletionContext, IRobotDocument +from robotframework_ls.impl.protocols import ( + ICompletionContext, + IRobotDocument, + IVariablesCollector, + IVariableFound, +) from robocorp_ls_core.robotframework_log import get_logger -from typing import Any +from robocorp_ls_core.protocols import check_implements +from typing import Optional log = get_logger(__name__) -class IVariableFound(object): - """ - :ivar variable_name: - :ivar variable_value: - :ivar completion_context: - This may be a new completion context, created when a new document is - being analyzed (the variable was created for that completion context). - :ivar source: - Source where the variable was found. - :ivar lineno: - Line where it was found (0-based). - """ - - variable_name = "" - variable_value = "" - completion_context = None - source = "" - - # Note: line/offsets 0-based. - lineno = -1 - end_lineno = -1 - col_offset = -1 - end_col_offset = -1 - - class _VariableFoundFromToken(object): def __init__( self, completion_context, variable_token, variable_value, variable_name=None @@ -72,6 +53,9 @@ def col_offset(self): def end_col_offset(self): return self.variable_token.end_col_offset + def __typecheckself__(self) -> None: + _: IVariableFound = check_implements(self) + class _VariableFoundFromPythonAst(object): def __init__( @@ -89,7 +73,7 @@ def __init__( self.end_lineno = end_lineno self.end_col_offset = end_col - self.completion_context = None + self.completion_context: Optional[ICompletionContext] = None self._path = path self.variable_name = variable_name self.variable_value = variable_value @@ -99,6 +83,9 @@ def __init__( def source(self): return self._path + def __typecheckself__(self) -> None: + _: IVariableFound = check_implements(self) + class _VariableFoundFromSettings(object): def __init__(self, variable_name, variable_value): @@ -127,6 +114,9 @@ def col_offset(self): def end_col_offset(self): return 0 + def __typecheckself__(self) -> None: + _: IVariableFound = check_implements(self) + class _VariableFoundFromBuiltins(_VariableFoundFromSettings): pass @@ -230,7 +220,7 @@ def _collect_completions_from_ast( def _collect_current_doc_variables( - completion_context: ICompletionContext, collector: _Collector + completion_context: ICompletionContext, collector: IVariablesCollector ): """ :param CompletionContext completion_context: @@ -242,16 +232,18 @@ def _collect_current_doc_variables( def _collect_resource_imports_variables( - completion_context: ICompletionContext, collector: _Collector + completion_context: ICompletionContext, collector: IVariablesCollector ): - resource_doc: IRobotDocument - for resource_doc in completion_context.get_resource_imports_as_docs(): + resource_doc: Optional[IRobotDocument] + for _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) _collect_following_imports(new_ctx, collector) def _collect_variable_imports_variables( - completion_context: ICompletionContext, collector: _Collector + completion_context: ICompletionContext, collector: IVariablesCollector ): variable_import_doc: IRobotDocument for variable_import_doc in completion_context.get_variable_imports_as_docs(): @@ -315,7 +307,7 @@ def _collect_variable_imports_variables( def _collect_following_imports( - completion_context: ICompletionContext, collector: _Collector + completion_context: ICompletionContext, collector: IVariablesCollector ): completion_context.check_cancelled() if completion_context.memo.follow_import_variables(completion_context.doc.uri): @@ -328,7 +320,9 @@ def _collect_following_imports( _collect_variable_imports_variables(completion_context, collector) -def _collect_arguments(completion_context: ICompletionContext, collector: _Collector): +def _collect_arguments( + completion_context: ICompletionContext, collector: IVariablesCollector +): from robotframework_ls.impl import ast_utils current_token_info = completion_context.get_current_token() @@ -352,7 +346,7 @@ def _convert_name_to_var(variable_name): def _collect_from_settings( - completion_context: ICompletionContext, collector: _Collector + completion_context: ICompletionContext, collector: IVariablesCollector ): from robotframework_ls.impl.robot_lsp_constants import OPTION_ROBOT_VARIABLES @@ -366,7 +360,7 @@ def _collect_from_settings( def _collect_from_builtins( - completion_context: ICompletionContext, collector: _Collector + completion_context: ICompletionContext, collector: IVariablesCollector ): from robotframework_ls.impl.robot_constants import BUILTIN_VARIABLES @@ -376,7 +370,9 @@ def _collect_from_builtins( collector.on_variable(_VariableFoundFromBuiltins(key, val)) -def collect_variables(completion_context: ICompletionContext, collector: _Collector): +def collect_variables( + completion_context: ICompletionContext, collector: IVariablesCollector +): from robotframework_ls.impl import ast_utils token_info = completion_context.get_current_token() diff --git a/robotframework-ls/tests/robotframework_ls_tests/test_code_analysis.py b/robotframework-ls/tests/robotframework_ls_tests/test_code_analysis.py index d36e5454b1..401ee54666 100644 --- a/robotframework-ls/tests/robotframework_ls_tests/test_code_analysis.py +++ b/robotframework-ls/tests/robotframework_ls_tests/test_code_analysis.py @@ -206,8 +206,23 @@ def test_resource_does_not_exist(workspace, libspec_manager, data_regression): from robotframework_ls.robot_config import RobotConfig config = RobotConfig() - # Note: we don't give errors if we can't resolve a resource. - _collect_errors(workspace, doc, data_regression, basename="no_error", config=config) + _collect_errors(workspace, doc, data_regression, config=config) + + +def test_report_wrong_library(workspace, libspec_manager, data_regression): + workspace.set_root("case4", libspec_manager=libspec_manager) + doc = workspace.put_doc( + "case4.robot", + """*** Settings *** +Library DoesNotExist +Resource DoesNotExist +""", + ) + + from robotframework_ls.robot_config import RobotConfig + + config = RobotConfig() + _collect_errors(workspace, doc, data_regression, config=config) def test_casing_on_filename(workspace, libspec_manager, data_regression): diff --git a/robotframework-ls/tests/robotframework_ls_tests/test_code_analysis/test_report_wrong_library.yml b/robotframework-ls/tests/robotframework_ls_tests/test_code_analysis/test_report_wrong_library.yml new file mode 100644 index 0000000000..4ec8310c66 --- /dev/null +++ b/robotframework-ls/tests/robotframework_ls_tests/test_code_analysis/test_report_wrong_library.yml @@ -0,0 +1,20 @@ +- message: 'Unresolved resource: DoesNotExist' + range: + end: + character: 24 + line: 2 + start: + character: 12 + line: 2 + severity: 1 + source: robotframework +- message: 'Unresolved library: DoesNotExist' + range: + end: + character: 23 + line: 1 + start: + character: 11 + line: 1 + severity: 1 + source: robotframework diff --git a/robotframework-ls/tests/robotframework_ls_tests/test_code_analysis/test_resource_does_not_exist.yml b/robotframework-ls/tests/robotframework_ls_tests/test_code_analysis/test_resource_does_not_exist.yml new file mode 100644 index 0000000000..33dffad58a --- /dev/null +++ b/robotframework-ls/tests/robotframework_ls_tests/test_code_analysis/test_resource_does_not_exist.yml @@ -0,0 +1,110 @@ +- message: 'Unresolved resource: does_not_exist.txt' + range: + end: + character: 30 + line: 5 + start: + character: 12 + line: 5 + severity: 1 + source: robotframework +- message: 'Unresolved resource: ${foo}/does_not_exist.txt' + range: + end: + character: 37 + line: 6 + start: + character: 12 + line: 6 + severity: 1 + source: robotframework +- message: 'Unresolved resource: ../does_not_exist.txt' + range: + end: + character: 33 + line: 7 + start: + character: 12 + line: 7 + severity: 1 + source: robotframework +- message: 'Unresolved resource: .' + range: + end: + character: 13 + line: 8 + start: + character: 12 + line: 8 + severity: 1 + source: robotframework +- message: 'Unresolved resource: ..' + range: + end: + character: 14 + line: 9 + start: + character: 12 + line: 9 + severity: 1 + source: robotframework +- message: 'Unresolved resource: ../' + range: + end: + character: 15 + line: 10 + start: + character: 12 + line: 10 + severity: 1 + source: robotframework +- message: 'Unresolved resource: ../../does_not_exist.txt' + range: + end: + character: 36 + line: 11 + start: + character: 12 + line: 11 + severity: 1 + source: robotframework +- message: 'Unresolved library: DoesNotExist' + range: + end: + character: 23 + line: 1 + start: + character: 11 + line: 1 + severity: 1 + source: robotframework +- message: 'Unresolved library: .' + range: + end: + character: 12 + line: 2 + start: + character: 11 + line: 2 + severity: 1 + source: robotframework +- message: 'Unresolved library: ..' + range: + end: + character: 13 + line: 3 + start: + character: 11 + line: 3 + severity: 1 + source: robotframework +- message: 'Unresolved library: ../' + range: + end: + character: 14 + line: 4 + start: + character: 11 + line: 4 + severity: 1 + source: robotframework