mirror of
https://github.com/davidhalter/parso.git
synced 2025-12-09 22:25:53 +08:00
Move the first issue to the new rule engine.
This commit is contained in:
@@ -1,22 +1,32 @@
|
||||
from contextlib import contextmanager
|
||||
|
||||
from parso._compatibility import use_metaclass
|
||||
|
||||
class Normalizer(object):
|
||||
_rule_value_classes = {}
|
||||
_rule_type_classes = {}
|
||||
|
||||
class _NormalizerMeta(type):
|
||||
def __new__(cls, name, bases, dct):
|
||||
new_cls = type.__new__(cls, name, bases, dct)
|
||||
new_cls.rule_value_classes = {}
|
||||
new_cls.rule_type_classes = {}
|
||||
return new_cls
|
||||
|
||||
|
||||
class Normalizer(use_metaclass(_NormalizerMeta)):
|
||||
def __init__(self, grammar, config):
|
||||
self._grammar = grammar
|
||||
self._config = config
|
||||
self.issues = []
|
||||
|
||||
self._rule_type_instances = self._instantiate_rules(self._rule_type_classes)
|
||||
self._rule_value_instances = self._instantiate_rules(self._rule_value_classes)
|
||||
self._rule_type_instances = self._instantiate_rules('rule_type_classes')
|
||||
self._rule_value_instances = self._instantiate_rules('rule_value_classes')
|
||||
|
||||
def _instantiate_rules(self, rules_map):
|
||||
def _instantiate_rules(self, attr):
|
||||
dct = {}
|
||||
for type_, rule_classes in rules_map.items():
|
||||
dct[type_] = [rule_cls(self) for rule_cls in rule_classes]
|
||||
for base in type(self).mro():
|
||||
rules_map = getattr(base, attr, {})
|
||||
for type_, rule_classes in rules_map.items():
|
||||
new = [rule_cls(self) for rule_cls in rule_classes]
|
||||
dct.setdefault(type_, []).extend(new)
|
||||
return dct
|
||||
|
||||
def walk(self, node):
|
||||
@@ -36,14 +46,15 @@ class Normalizer(object):
|
||||
|
||||
@contextmanager
|
||||
def visit_node(self, node):
|
||||
self._check_type_rules(node)
|
||||
yield
|
||||
|
||||
def _check_type_rules(self, node):
|
||||
for rule in self._rule_type_instances.get(node.type, []):
|
||||
rule.feed_node(node)
|
||||
|
||||
yield
|
||||
|
||||
def visit_leaf(self, leaf):
|
||||
for rule in self._rule_type_instances.get(leaf.type, []):
|
||||
rule.feed_node(leaf)
|
||||
self._check_type_rules(leaf)
|
||||
|
||||
for rule in self._rule_value_instances.get(leaf.value, []):
|
||||
rule.feed_node(leaf)
|
||||
@@ -81,9 +92,9 @@ class Normalizer(object):
|
||||
|
||||
def decorator(rule_cls):
|
||||
if value is not None:
|
||||
cls._rule_value_classes.setdefault(value, []).append(rule_cls)
|
||||
cls.rule_value_classes.setdefault(value, []).append(rule_cls)
|
||||
if type is not None:
|
||||
cls._rule_type_classes.setdefault(type, []).append(rule_cls)
|
||||
cls.rule_type_classes.setdefault(type, []).append(rule_cls)
|
||||
return rule_cls
|
||||
|
||||
return decorator
|
||||
@@ -133,20 +144,24 @@ class Rule(object):
|
||||
def get_node(self, node):
|
||||
return node
|
||||
|
||||
def _get_message(self, message):
|
||||
if message is None:
|
||||
message = self.message
|
||||
if message is None:
|
||||
raise ValueError("The message on the class is not set.")
|
||||
return message
|
||||
|
||||
def add_issue(self, node, code=None, message=None):
|
||||
if code is None:
|
||||
code = self.code
|
||||
if code is None:
|
||||
raise ValueError("The error code on the class is not set.")
|
||||
|
||||
if message is None:
|
||||
message = self.message
|
||||
if message is None:
|
||||
raise ValueError("The message on the class is not set.")
|
||||
message = self._get_message(message)
|
||||
|
||||
self._normalizer.add_issue(node, code, message)
|
||||
|
||||
def feed_node(self, node):
|
||||
if self.check(node):
|
||||
if self.is_issue(node):
|
||||
issue_node = self.get_node(node)
|
||||
self.add_issue(issue_node)
|
||||
|
||||
@@ -270,7 +270,7 @@ class ErrorFinder(Normalizer):
|
||||
return _Context(node, self._add_syntax_error, parent_context)
|
||||
return parent_context
|
||||
|
||||
self._context = create_context(node) or _Context(node, self._add_syntax_error)
|
||||
self.context = create_context(node) or _Context(node, self._add_syntax_error)
|
||||
self._indentation_count = 0
|
||||
|
||||
def visit(self, node):
|
||||
@@ -285,6 +285,8 @@ class ErrorFinder(Normalizer):
|
||||
|
||||
@contextmanager
|
||||
def visit_node(self, node):
|
||||
self._check_type_rules(node)
|
||||
|
||||
if node.type == 'error_node':
|
||||
leaf = node.get_next_leaf()
|
||||
if node.children[-1].type == 'newline':
|
||||
@@ -312,8 +314,8 @@ class ErrorFinder(Normalizer):
|
||||
if expr_list.type != 'expr_list': # Already handled.
|
||||
self._check_assignment(expr_list)
|
||||
|
||||
with self._context.add_block(node):
|
||||
if len(self._context.blocks) == _MAX_BLOCK_SIZE:
|
||||
with self.context.add_block(node):
|
||||
if len(self.context.blocks) == _MAX_BLOCK_SIZE:
|
||||
self._add_syntax_error("too many statically nested blocks", node)
|
||||
yield
|
||||
return
|
||||
@@ -338,7 +340,7 @@ class ErrorFinder(Normalizer):
|
||||
message = "future feature %s is not defined" % name
|
||||
self._add_syntax_error(message, node)
|
||||
elif node.type == 'import_from':
|
||||
if node.is_star_import() and self._context.parent_context is not None:
|
||||
if node.is_star_import() and self.context.parent_context is not None:
|
||||
message = "import * only allowed at module level"
|
||||
self._add_syntax_error(message, node)
|
||||
elif node.type == 'import_as_names':
|
||||
@@ -404,7 +406,7 @@ class ErrorFinder(Normalizer):
|
||||
self._check_assignment(expr_list)
|
||||
|
||||
if node.children[0] == 'async' \
|
||||
and not self._context.is_async_funcdef():
|
||||
and not self.context.is_async_funcdef():
|
||||
message = "asynchronous comprehension outside of an asynchronous function"
|
||||
self._add_syntax_error(message, node)
|
||||
elif node.type == 'arglist':
|
||||
@@ -529,12 +531,12 @@ class ErrorFinder(Normalizer):
|
||||
message = "keyword can't be an expression"
|
||||
self._add_syntax_error(message, first)
|
||||
elif node.type == 'nonlocal_stmt':
|
||||
if self._context.parent_context is None:
|
||||
if self.context.parent_context is None:
|
||||
message = "nonlocal declaration not allowed at module level"
|
||||
self._add_syntax_error(message, node)
|
||||
elif self._context.is_function():
|
||||
elif self.context.is_function():
|
||||
for nonlocal_name in node.children[1::2]:
|
||||
param_names = [p.name.value for p in self._context.node.get_params()]
|
||||
param_names = [p.name.value for p in self.context.node.get_params()]
|
||||
if nonlocal_name.value == node:
|
||||
pass
|
||||
elif node.type == 'expr_stmt':
|
||||
@@ -565,9 +567,9 @@ class ErrorFinder(Normalizer):
|
||||
if node.type == 'suite':
|
||||
self._indentation_count -= 1
|
||||
elif node.type in ('classdef', 'funcdef'):
|
||||
context = self._context
|
||||
self._context = context.parent_context
|
||||
self._context.close_child_context(context)
|
||||
context = self.context
|
||||
self.context = context.parent_context
|
||||
self.context.close_child_context(context)
|
||||
|
||||
def visit_leaf(self, leaf):
|
||||
if leaf.type == 'error_leaf':
|
||||
@@ -604,7 +606,7 @@ class ErrorFinder(Normalizer):
|
||||
if leaf.value == 'None' and self._version < (3, 0) and leaf.is_definition():
|
||||
self._add_syntax_error('cannot assign to None', leaf)
|
||||
|
||||
self._context.add_name(leaf)
|
||||
self.context.add_name(leaf)
|
||||
elif leaf.type == 'string':
|
||||
string_prefix = leaf.string_prefix.lower()
|
||||
if 'b' in string_prefix \
|
||||
@@ -642,7 +644,7 @@ class ErrorFinder(Normalizer):
|
||||
|
||||
elif leaf.value == 'continue':
|
||||
in_loop = False
|
||||
for block in self._context.blocks:
|
||||
for block in self.context.blocks:
|
||||
if block.type == 'for_stmt':
|
||||
in_loop = True
|
||||
if block.type == 'try_stmt':
|
||||
@@ -655,27 +657,24 @@ class ErrorFinder(Normalizer):
|
||||
self._add_syntax_error(message, leaf)
|
||||
elif leaf.value == 'break':
|
||||
in_loop = False
|
||||
for block in self._context.blocks:
|
||||
for block in self.context.blocks:
|
||||
if block.type in ('for_stmt', 'while_stmt'):
|
||||
in_loop = True
|
||||
if not in_loop:
|
||||
self._add_syntax_error("'break' outside loop", leaf)
|
||||
elif leaf.value in ('yield', 'return'):
|
||||
if self._context.node.type != 'funcdef':
|
||||
if self.context.node.type != 'funcdef':
|
||||
self._add_syntax_error("'%s' outside function" % leaf.value, leaf.parent)
|
||||
elif self._context.is_async_funcdef() \
|
||||
and any(self._context.node.iter_yield_exprs()):
|
||||
elif self.context.is_async_funcdef() \
|
||||
and any(self.context.node.iter_yield_exprs()):
|
||||
if leaf.value == 'return' and leaf.parent.type == 'return_stmt':
|
||||
self._add_syntax_error("'return' with value in async generator", leaf.parent)
|
||||
elif leaf.value == 'yield' \
|
||||
and leaf.get_next_leaf() != 'from' \
|
||||
and self._version == (3, 5):
|
||||
self._add_syntax_error("'yield' inside async function", leaf.parent)
|
||||
elif leaf.value == 'await':
|
||||
if not self._context.is_async_funcdef():
|
||||
self._add_syntax_error("'await' outside async function", leaf.parent)
|
||||
elif leaf.value == 'from' and leaf.parent.type == 'yield_arg' \
|
||||
and self._context.is_async_funcdef():
|
||||
and self.context.is_async_funcdef():
|
||||
yield_ = leaf.parent.parent
|
||||
self._add_syntax_error("'yield from' inside async function", yield_)
|
||||
elif leaf.value == '*':
|
||||
@@ -698,10 +697,10 @@ class ErrorFinder(Normalizer):
|
||||
elif leaf.value == ':':
|
||||
parent = leaf.parent
|
||||
if parent.type in ('classdef', 'funcdef'):
|
||||
self._context = self._context.add_context(parent)
|
||||
self.context = self.context.add_context(parent)
|
||||
|
||||
|
||||
return ''
|
||||
return super(ErrorFinder, self).visit_leaf(leaf)
|
||||
|
||||
def _check_assignment(self, node, is_deletion=False):
|
||||
error = None
|
||||
@@ -775,7 +774,7 @@ class ErrorFinder(Normalizer):
|
||||
self._error_dict.setdefault(line, args)
|
||||
|
||||
def finalize(self):
|
||||
self._context.finalize()
|
||||
self.context.finalize()
|
||||
|
||||
for code, message, node in self._error_dict.values():
|
||||
self.issues.append(Issue(node, code, message))
|
||||
@@ -788,6 +787,10 @@ class ErrorFinderConfig(NormalizerConfig):
|
||||
class SyntaxRule(Rule):
|
||||
code = 901
|
||||
|
||||
def _get_message(self, message):
|
||||
message = super(SyntaxRule, self)._get_message(message)
|
||||
return "SyntaxError: " + message
|
||||
|
||||
|
||||
class IndentationRule(Rule):
|
||||
code = 903
|
||||
@@ -797,8 +800,8 @@ class IndentationRule(Rule):
|
||||
class AwaitOutsideAsync(SyntaxRule):
|
||||
message = "'await' outside async function"
|
||||
|
||||
def check_leaf_value(self, leaf):
|
||||
return not self._context.is_async_funcdef()
|
||||
def is_issue(self, leaf):
|
||||
return not self._normalizer.context.is_async_funcdef()
|
||||
|
||||
def get_error_node(self, node):
|
||||
# Return the whole await statement.
|
||||
|
||||
Reference in New Issue
Block a user