Skip to content

Commit 8eddbb1

Browse files
author
whitequark
committedMay 8, 2015
Also test diagnostics.
1 parent ccbcb3b commit 8eddbb1

File tree

2 files changed

+109
-22
lines changed

2 files changed

+109
-22
lines changed
 

‎pyparser/parser.py

+19-15
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ def rule(parser):
144144

145145
error_tok = parser._tokens[parser._errindex]
146146
error = diagnostic.Diagnostic(
147-
"error", "unexpected {actual}: expected {expected}",
147+
"fatal", "unexpected {actual}: expected {expected}",
148148
{'actual': error_tok.kind, 'expected': expected},
149149
error_tok.loc)
150150
raise diagnostic.DiagnosticException(error)
@@ -360,9 +360,6 @@ def _save(self):
360360
return self._index
361361

362362
def _restore(self, data, rule):
363-
if self._index == data:
364-
return
365-
366363
self._index = data
367364
self._token = self._tokens[self._index]
368365

@@ -411,10 +408,10 @@ def _assignable(self, node, is_delete=False):
411408
else:
412409
if is_delete:
413410
error = diagnostic.Diagnostic(
414-
"error", "cannot delete this expression", {}, node.loc)
411+
"fatal", "cannot delete this expression", {}, node.loc)
415412
else:
416413
error = diagnostic.Diagnostic(
417-
"error", "cannot assign to this expression", {}, node.loc)
414+
"fatal", "cannot assign to this expression", {}, node.loc)
418415
raise diagnostic.DiagnosticException(error)
419416

420417
def _empty_arguments(self):
@@ -537,16 +534,18 @@ def varargslist(self, fparams, args):
537534
('*' NAME [',' '**' NAME] | '**' NAME) |
538535
fpdef ['=' test] (',' fpdef ['=' test])* [','])"""
539536
for fparam, default_opt in fparams:
540-
args.args.append(fparam)
541537
if default_opt:
542538
equals_loc, default = default_opt
543539
args.equals_locs.append(equals_loc)
544540
args.defaults.append(default)
545541
elif len(args.defaults) > 0:
546542
error = diagnostic.Diagnostic(
547-
"error", "non-default argument follows default argument", {}, fparam.loc)
543+
"fatal", "non-default argument follows default argument", {},
544+
fparam.loc, [args.args[-1].loc.join(args.defaults[-1].loc)])
548545
raise diagnostic.DiagnosticException(error)
549546

547+
args.args.append(fparam)
548+
550549
def fparam_loc(fparam, default_opt):
551550
if default_opt:
552551
equals_loc, default = default_opt
@@ -603,9 +602,10 @@ def expr_stmt(self, lhs, rhs):
603602
"""expr_stmt: testlist (augassign (yield_expr|testlist) |
604603
('=' (yield_expr|testlist))*)"""
605604
if isinstance(rhs, ast.AugAssign):
606-
if isinstance(lhs, ast.Tuple):
605+
if isinstance(lhs, ast.Tuple) or isinstance(lhs, ast.List):
607606
error = diagnostic.Diagnostic(
608-
"error", "illegal expression for augmented assignment", {}, rhs.loc)
607+
"fatal", "illegal expression for augmented assignment", {},
608+
rhs.op.loc, [lhs.loc])
609609
raise diagnostic.DiagnosticException(error)
610610
else:
611611
rhs.target = self._assignable(lhs)
@@ -859,14 +859,16 @@ def if_stmt(self, if_loc, test, if_colon_loc, body, elifs, else_opt):
859859
for elif_ in elifs:
860860
stmt.keyword_loc, stmt.test, stmt.if_colon_loc, stmt.body = elif_
861861
stmt.loc = stmt.keyword_loc.join(stmt.body[-1].loc)
862-
if stmt.orelse: stmt.loc = stmt.loc.join(stmt.orelse[-1].loc)
862+
if stmt.orelse:
863+
stmt.loc = stmt.loc.join(stmt.orelse[-1].loc)
863864
stmt = ast.If(orelse=[stmt],
864865
else_loc=None, else_colon_loc=None)
865866

866867
stmt.keyword_loc, stmt.test, stmt.if_colon_loc, stmt.body = \
867868
if_loc, test, if_colon_loc, body
868869
stmt.loc = stmt.keyword_loc.join(stmt.body[-1].loc)
869-
if stmt.orelse: stmt.loc = stmt.loc.join(stmt.orelse[-1].loc)
870+
if stmt.orelse:
871+
stmt.loc = stmt.loc.join(stmt.orelse[-1].loc)
870872
return stmt
871873

872874
@action(Seq(Loc('while'), Rule('test'), Loc(':'), Rule('suite'),
@@ -1307,7 +1309,8 @@ def arglist_2(self, star_loc, stararg, postargs, kwarg_opt):
13071309
for postarg in postargs:
13081310
if not isinstance(postarg, ast.keyword):
13091311
error = diagnostic.Diagnostic(
1310-
"error", "only named arguments may follow *expression", {}, postarg.loc)
1312+
"fatal", "only named arguments may follow *expression", {},
1313+
postarg.loc, [star_loc.join(stararg.loc)])
13111314
raise diagnostic.DiagnosticException(error)
13121315

13131316
return postargs, \
@@ -1344,7 +1347,8 @@ def arglist(self, pre_args, rest):
13441347
call.keywords.append(arg)
13451348
elif len(call.keywords) > 0:
13461349
error = diagnostic.Diagnostic(
1347-
"error", "non-keyword arg after keyword arg", {}, arg.loc)
1350+
"fatal", "non-keyword arg after keyword arg", {},
1351+
arg.loc, [call.keywords[-1].loc])
13481352
raise diagnostic.DiagnosticException(error)
13491353
else:
13501354
call.args.append(arg)
@@ -1355,7 +1359,7 @@ def argument_1(self, equals_loc, rhs):
13551359
def thunk(lhs):
13561360
if not isinstance(lhs, ast.Name):
13571361
error = diagnostic.Diagnostic(
1358-
"error", "keyword must be an identifier", {}, lhs.loc)
1362+
"fatal", "keyword must be an identifier", {}, lhs.loc)
13591363
raise diagnostic.DiagnosticException(error)
13601364
return ast.keyword(arg=lhs.id, value=rhs,
13611365
loc=lhs.loc.join(rhs.loc),

‎pyparser/test/test_parser.py

+90-7
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def flatten_python_ast(self, node):
6969
_loc_re = re.compile(r"\s*([~^]*)<?\s+([a-z_0-9.]+)")
7070
_path_re = re.compile(r"(([a-z_]+)|([0-9]+))(\.)?")
7171

72-
def match_loc(self, ast, matcher, root=lambda x: x):
72+
def match_loc(self, ast, matcher, root=lambda x: (0, x)):
7373
offset, ast = root(ast)
7474

7575
matcher_pos = 0
@@ -142,24 +142,24 @@ def assertParsesToplevel(self, expected_flat_ast, code,
142142
ast = getattr(self.parser_for(code, interactive=interactive), mode)()
143143
self.assertEqual(expected_flat_ast, self.flatten_ast(ast))
144144

145-
def assertDiagnoses(self, code, diag):
145+
def assertDiagnoses(self, code, level, reason, args={}, loc_matcher=""):
146146
try:
147147
self.parser_for(code).file_input()
148148
self.fail("Expected a diagnostic")
149149
except diagnostic.DiagnosticException as e:
150-
level, reason, args, loc = diag
151150
self.assertEqual(level, e.diagnostic.level)
152151
self.assertEqual(reason, e.diagnostic.reason)
153152
for key in args:
154153
self.assertEqual(args[key], e.diagnostic.arguments[key],
155154
"{{%s}}: \"%s\" != \"%s\"" %
156155
(key, args[key], e.diagnostic.arguments[key]))
157-
self.assertEqual(source.Range(self.source_buffer, *loc),
158-
e.diagnostic.location)
156+
self.match_loc([e.diagnostic.location] + e.diagnostic.highlights,
157+
loc_matcher)
159158

160-
def assertDiagnosesUnexpected(self, code, err_token, loc):
159+
def assertDiagnosesUnexpected(self, code, err_token, loc_matcher=""):
161160
self.assertDiagnoses(code,
162-
("error", "unexpected {actual}: expected {expected}", {'actual': err_token}, loc))
161+
"fatal", "unexpected {actual}: expected {expected}",
162+
{'actual': err_token}, loc_matcher="")
163163

164164
# Fixtures
165165

@@ -806,6 +806,14 @@ def test_assign(self):
806806
"x = yield y",
807807
"~~~~~~~~~~~ 0.loc")
808808

809+
def test_assign_tuplerhs(self):
810+
self.assertParsesSuite(
811+
[{'ty': 'Assign', 'targets': [self.ast_x], 'value':
812+
{'ty': 'Tuple', 'ctx': None, 'elts': [self.ast_1, self.ast_2]}}],
813+
"x = 1, 2",
814+
" ~~~~ 0.value.loc"
815+
"~~~~~~~~ 0.loc")
816+
809817
def test_augassign(self):
810818
self.assertParsesSuite(
811819
[{'ty': 'AugAssign', 'op': {'ty': 'Add'}, 'target': self.ast_x, 'value': self.ast_1}],
@@ -905,6 +913,13 @@ def test_print(self):
905913
" ~~ 0.dest_loc"
906914
"~~~~~~~~~~~~ 0.loc")
907915

916+
self.assertParsesSuite(
917+
[{'ty': 'Print', 'dest': self.ast_2, 'values': [self.ast_1], 'nl': False}],
918+
"print >>2, 1,",
919+
"~~~~~ 0.keyword_loc"
920+
" ~~ 0.dest_loc"
921+
"~~~~~~~~~~~~~ 0.loc")
922+
908923
def test_del(self):
909924
self.assertParsesSuite(
910925
[{'ty': 'Delete', 'targets': [self.ast_x]}],
@@ -1520,3 +1535,71 @@ def test_future_print(self):
15201535
{'ty': 'Call', 'func': {'ty': 'Name', 'id': 'print', 'ctx': None},
15211536
'starargs': None, 'kwargs': None, 'args': [self.ast_x], 'keywords': []}}],
15221537
"from __future__ import print_function·print(x)")
1538+
1539+
#
1540+
# DIAGNOSTICS
1541+
#
1542+
1543+
def test_diag_assignable(self):
1544+
self.assertDiagnoses(
1545+
"1 = 1",
1546+
'fatal', "cannot assign to this expression", {},
1547+
"^ 0")
1548+
1549+
self.assertDiagnoses(
1550+
"[1] = 1",
1551+
'fatal', "cannot assign to this expression", {},
1552+
" ^ 0")
1553+
1554+
self.assertDiagnoses(
1555+
"x() = 1",
1556+
'fatal', "cannot assign to this expression", {},
1557+
"~~~ 0")
1558+
1559+
self.assertDiagnoses(
1560+
"del 1",
1561+
'fatal', "cannot delete this expression", {},
1562+
" ^ 0")
1563+
1564+
def test_diag_def(self):
1565+
self.assertDiagnoses(
1566+
"def x(y=1, z): pass",
1567+
'fatal', "non-default argument follows default argument", {},
1568+
" ^ 0"
1569+
" ~~~ 1")
1570+
1571+
def test_diag_augassign(self):
1572+
self.assertDiagnoses(
1573+
"(1,) += 1",
1574+
'fatal', "illegal expression for augmented assignment", {},
1575+
" ^^ 0"
1576+
"~~~~ 1")
1577+
1578+
self.assertDiagnoses(
1579+
"[1] += 1",
1580+
'fatal', "illegal expression for augmented assignment", {},
1581+
" ^^ 0"
1582+
"~~~ 1")
1583+
1584+
def test_diag_call(self):
1585+
self.assertDiagnoses(
1586+
"x(*y, z)",
1587+
'fatal', "only named arguments may follow *expression", {},
1588+
" ^ 0"
1589+
" ~~ 1")
1590+
1591+
self.assertDiagnoses(
1592+
"x(y=1, z)",
1593+
'fatal', "non-keyword arg after keyword arg", {},
1594+
" ^ 0"
1595+
" ~~~ 1")
1596+
1597+
self.assertDiagnoses(
1598+
"x(1=1)",
1599+
'fatal', "keyword must be an identifier", {},
1600+
" ^ 0")
1601+
1602+
def test_diag_generic(self):
1603+
self.assertDiagnosesUnexpected(
1604+
"x + ,", ",",
1605+
" ^ 0")

0 commit comments

Comments
 (0)
Please sign in to comment.