def find_block_end(node): """ Find where the keyword/test case/block ends. If there are only comments and new lines left, the first comment that starts from col 1 is considered outside your block:: Keyword Line # comment Other Line # comment belonging to Keyword # This should not belong to Keyword # Since there was comment starting from col 1, this comment is also outside block """ for index, child in reversed(list(enumerate(node.body))): node_type = getattr(child, 'type', '') if not node_type: return len(node.body) - 1 if node_type not in (Token.COMMENT, Token.EOL): break else: return len(node.body) - 1 for block_index, child in enumerate(node.body[index + 1:]): if getattr(child, 'type', 'invalid') == Token.COMMENT and token_col( child, Token.COMMENT) == 1: return block_index + index + 1 return len(node.body) - 1
def check_standalone_comments_indent(self, node): for child in node.body: if getattr(child, 'type', '') == Token.COMMENT and \ getattr(child, 'tokens', None) and child.tokens[0].type == Token.SEPARATOR: self.report("uneven-indent", 'over', node=child, col=token_col(child, Token.COMMENT)) self.generic_visit(node)
def check_statement_in_loop(self, node, token_type): if self.loops or node.errors and f"{token_type} can only be used inside a loop." not in node.errors: return self.report( "statement-outside-loop", name=token_type, statement_type="statement", node=node, col=token_col(node, token_type), )
def check_bdd_keywords(self, keyword_name, node): if keyword_name.lower() not in self.bdd or isinstance(node, Keyword): return arg = node.get_token(Token.ARGUMENT) suffix = f". Use one space between: '{keyword_name.title()} {arg.value}'" if arg else "" col = token_col(node, Token.NAME, Token.KEYWORD) self.report("bdd-without-keyword-call", keyword_name=keyword_name, error_msg=suffix, node=node, col=col)
def validate_standalone_comments(self, comments_and_eols): """ Report any comment that does not start from col 1. :param comments_and_eols: list of comments and empty lines (outside keyword and test case definitions) """ for child in comments_and_eols: if getattr(child, 'type', 'invalid') != Token.COMMENT: continue col = token_col(child, Token.COMMENT) if col != 1: self.report('uneven-indent', 'over', node=child, col=col)
def visit_Return(self, node): # noqa """For RETURN use visit_ReturnStatement - visit_Return will most likely visit RETURN in the future""" if ROBOT_VERSION.major < 5: return self.report( "deprecated-statement", statement_name="[Return]", alternative="RETURN", node=node, col=token_col(node, Token.RETURN), version="5.*", )
def check_standalone_comments_indent(self, node): # comments before first test case / keyword for child in node.body: if (getattr(child, "type", "") == Token.COMMENT and getattr(child, "tokens", None) and child.tokens[0].type == Token.SEPARATOR): self.report( "uneven-indent", over_or_under="over", node=child, col=token_col(child, Token.COMMENT), ) self.generic_visit(node)
def check_if_keyword_is_deprecated(self, keyword_name, node): normalized_keyword_name = normalize_robot_name( keyword_name, remove_prefix="builtin.") deprecated_statements = self.deprecated_keywords.get( ROBOT_VERSION.major, {}) if normalized_keyword_name in deprecated_statements: alternative = deprecated_statements[normalized_keyword_name] col = token_col(node, Token.NAME, Token.KEYWORD) self.report( "deprecated-statement", statement_name=keyword_name, alternative=alternative, node=node, col=col, version=f"{ROBOT_VERSION.major}.*", )
def handle_error(self, node, error, error_index=0): # noqa if not error: return if any(should_ignore in error for should_ignore in self.ignore_errors): return if "Invalid argument syntax" in error: self.handle_invalid_syntax(node, error) elif "Non-existing setting" in error: self.handle_invalid_setting(node, error) elif "Invalid variable name" in error: self.handle_invalid_variable(node, error) elif "RETURN can only be used inside" in error: self.report("return-in-test-case", node=node, col=token_col(node, "RETURN STATEMENT")) elif "IF" in error or ("ELSE" in error and If and isinstance(self.in_block, If)): self.handle_invalid_block(node, error, "invalid-if") elif "FOR loop" in error: self.handle_invalid_block(node, error, "invalid-for-loop") elif "Non-default argument after default arguments" in error or "Only last argument can be kwargs" in error: self.handle_positional_after_named(node, error_index) elif "is allowed only once. Only the first value is used" in error: return else: error = error.replace("\n ", "") self.report("parsing-error", error_msg=error, node=node)