class ProhibitCommandRelyOnUser(AbstractPolicy): description = 'Avoid commands that rely on user settings' reference = get_reference_source('FRAGILE') level = Level.WARNING def listen_node_types(self): return [NodeType.EXCMD] def is_valid(self, node, lint_context): """ Whether the specified node is valid. This policy prohibit following commands: - normal without ! - substitute """ ea = node['ea'] command = ea['cmd'].get('name', None) # It seems line jump command if command is None: return True is_prohibited_command = command in ProhibitedCommands if is_prohibited_command: return False should_be_with_bang = command in CommandsShouldBeWithBang if not should_be_with_bang: return True is_bang = ea.get('forceit', 0) == 1 return is_bang
class ProhibitEqualTildeOperator(AbstractPolicy): level = Level.WARNING description = 'Use the =~# or =~? operator families over the =~ family.' reference = get_reference_source('MATCHING') def listen_node_types(self): return [ NodeType.EQUAL, NodeType.NEQUAL, NodeType.GREATER, NodeType.GEQUAL, NodeType.SMALLER, NodeType.SEQUAL, NodeType.MATCH, NodeType.NOMATCH, NodeType.IS, NodeType.ISNOT, ] def is_valid(self, node, lint_context): """ Whether the specified node is valid to the policy. In this policy, comparing between a string and any value by 'ignorecase'-sensitive is invalid. This policy can detect following script: variable =~ '1' But detecting exactly string comparison without evaluation is very hard. So this policy often get false-positive/negative results. False-positive case is: '1' =~ 1 False-negative case is: ('1') =~ 1 """ node_type = NodeType(node['type']) left_type = NodeType(node['left']['type']) right_type = NodeType(node['right']['type']) is_like_string_comparison = left_type is NodeType.STRING \ or right_type is NodeType.STRING is_valid = not is_like_string_comparison if not is_valid: self._make_description(node_type) return is_valid def _make_description(self, node_type): good_examples = [ '`' + op + '`' for op in GoodStringComparisonOperators[node_type] ] formatted_good_examples = ' or '.join(good_examples) bad_example = BadStringComparisonOperators[node_type] params = { 'good_example': formatted_good_examples, 'bad_example': bad_example, } self.description = ('Use robust operators {good_example} ' 'instead of `{bad_example}`').format(**params)
class ProhibitUnnecessaryDoubleQuote(AbstractPolicy): description = 'Prefer single quoted strings' reference = get_reference_source('STRINGS') level = Level.WARNING def listen_node_types(self): return [NodeType.STRING] def is_valid(self, node, lint_context): """ Whether the specified node is valid. In this policy, valid node is only 3 cases; - single quoted - double quoted, but including a special char - double quoted inside single quoted See `:help expr-string`. """ value = node['value'] is_double_quoted = value[0] == '"' if not is_double_quoted: return True has_escaped_char = _special_char_matcher.search(value) is not None if has_escaped_char: return True return is_on_string_expr_context(node)
class ProhibitCommandWithUnintendedSideEffect(AbstractPolicy): level = Level.WARNING description = 'Do not use a command that has unintended side effects' reference = get_reference_source('DANGEROUS') def listen_node_types(self): return [ NodeType.EXCMD, ] def is_valid(self, node, lint_context): """ Whether the specified node is valid to the policy. This policy prohibit using `:s[ubstitute]` family. """ command = node['ea']['cmd'].get('name', None) is_prohibited_command = any(pattern == command for pattern in PROHIBITED_COMMAND_PATTERNS) return not is_prohibited_command
class ProhibitNoAbortFunction(AbstractPolicy): description = 'Use the abort attribute and ! for functions in autoload' reference = get_reference_source('FUNCTIONS') level = Level.WARNING def listen_node_types(self): return [NodeType.FUNCTION] def is_valid(self, node, lint_context): """ Whether the specified node is valid. This policy prohibits functions in autoload that have no 'abort' or bang """ if 'autoload' not in lint_context['lint_target'].path.parts: return True has_bang = node['ea']['forceit'] != 0 has_abort = node['attr']['abort'] != 0 return has_bang and has_abort
def __init__(self): super(ProhibitCommandWithUnintendedSideEffect, self).__init__() self.level = Level.WARNING self.description = 'Do not use the command that has unintended side effect' self.reference = get_reference_source('DANGEROUS')
def __init__(self): super(ProhibitEqualTildeOperator, self).__init__() self.level = Level.WARNING self.description = 'Use the =~# or =~? operator families over the =~ family.' self.reference = get_reference_source('MATCHING')
def test_get_reference_source(self): actual_ref_source = get_reference_source('STRINGS') expected_ref_source = 'Google VimScript Style Guide (Strings)' self.assertEqual(actual_ref_source, expected_ref_source)
def __init__(self): super(ProhibitCommandRelyOnUser, self).__init__() self.description = 'Avoid commands that rely on user settings' self.reference = get_reference_source('FRAGILE') self.level = Level.WARNING
def __init__(self): super(ProhibitNoAbortFunction, self).__init__() self.description = 'Use abort attribute for functions in autoload/' self.reference = get_reference_source('FUNCTIONS') self.level = Level.WARNING
def __init__(self): super(ProhibitCommandWithUnintendedSideEffect, self).__init__() self.level = Level.WARNING self.description = 'Do not use a command that has unintended side effects' self.reference = get_reference_source('DANGEROUS')
def __init__(self): super(ProhibitCommandRelyOnUser, self).__init__() self.description = 'Prefer single quoted strings' self.reference = get_reference_source('FRAGILE') self.level = Level.WARNING
def __init__(self): super(ProhibitUnnecessaryDoubleQuote, self).__init__() self.description = 'Prefer single quoted strings' self.reference = get_reference_source('STRINGS') self.level = Level.WARNING
def __init__(self): super(ProhibitNoAbortFunction, self).__init__() self.description = 'Use the abort attribute for functions in autoload' self.reference = get_reference_source('FUNCTIONS') self.level = Level.WARNING