forked from M-Labs/artiq
Refactor error reporting in _unify to factor out custom notes.
This commit is contained in:
parent
ba9a7d087d
commit
b8ce3f85bd
|
@ -111,7 +111,7 @@ class LocalExtractor(algorithm.Visitor):
|
|||
break
|
||||
if not found:
|
||||
diag = diagnostic.Diagnostic("error",
|
||||
"can't declare name '{name}' as nonlocal: it is not bound in any outer scope",
|
||||
"cannot declare name '{name}' as nonlocal: it is not bound in any outer scope",
|
||||
{"name": name},
|
||||
loc, [node.keyword_loc])
|
||||
self.engine.process(diag)
|
||||
|
@ -132,51 +132,38 @@ class Inferencer(algorithm.Transformer):
|
|||
self.env_stack = []
|
||||
self.function = None # currently visited function
|
||||
|
||||
def _unify(self, typea, typeb, loca, locb, kind='generic'):
|
||||
def _unify(self, typea, typeb, loca, locb, makenotes=None):
|
||||
try:
|
||||
typea.unify(typeb)
|
||||
except types.UnificationError as e:
|
||||
printer = types.TypePrinter()
|
||||
|
||||
if kind == "expects":
|
||||
note1 = diagnostic.Diagnostic("note",
|
||||
"expression expecting an operand of type {typea}",
|
||||
{"typea": printer.name(typea)},
|
||||
loca)
|
||||
elif kind == "return_type" or kind == "return_type_none":
|
||||
note1 = diagnostic.Diagnostic("note",
|
||||
"function with return type {typea}",
|
||||
{"typea": printer.name(typea)},
|
||||
loca)
|
||||
if makenotes:
|
||||
notes = makenotes(printer, typea, typeb, loca, locb)
|
||||
else:
|
||||
note1 = diagnostic.Diagnostic("note",
|
||||
"expression of type {typea}",
|
||||
{"typea": printer.name(typea)},
|
||||
loca)
|
||||
|
||||
if kind == "return_type_none":
|
||||
note2 = diagnostic.Diagnostic("note",
|
||||
"implied expression of type {typeb}",
|
||||
{"typeb": printer.name(typeb)},
|
||||
locb)
|
||||
else:
|
||||
note2 = diagnostic.Diagnostic("note",
|
||||
"expression of type {typeb}",
|
||||
{"typeb": printer.name(typeb)},
|
||||
locb)
|
||||
notes = [
|
||||
diagnostic.Diagnostic("note",
|
||||
"expression of type {typea}",
|
||||
{"typea": printer.name(typea)},
|
||||
loca),
|
||||
diagnostic.Diagnostic("note",
|
||||
"expression of type {typeb}",
|
||||
{"typeb": printer.name(typeb)},
|
||||
locb)
|
||||
]
|
||||
|
||||
highlights = [locb] if locb else []
|
||||
if e.typea.find() == typea.find() and e.typeb.find() == typeb.find():
|
||||
diag = diagnostic.Diagnostic("fatal",
|
||||
diag = diagnostic.Diagnostic("error",
|
||||
"cannot unify {typea} with {typeb}",
|
||||
{"typea": printer.name(typea), "typeb": printer.name(typeb)},
|
||||
loca, highlights, notes=[note1, note2])
|
||||
loca, highlights, notes)
|
||||
else: # give more detail
|
||||
diag = diagnostic.Diagnostic("fatal",
|
||||
diag = diagnostic.Diagnostic("error",
|
||||
"cannot unify {typea} with {typeb}: {fraga} is incompatible with {fragb}",
|
||||
{"typea": printer.name(typea), "typeb": printer.name(typeb),
|
||||
"fraga": printer.name(e.typea), "fragb": printer.name(e.typeb)},
|
||||
loca, highlights, notes=[note1, note2])
|
||||
loca, highlights, notes)
|
||||
self.engine.process(diag)
|
||||
|
||||
def _find_name(self, name, loc):
|
||||
|
@ -227,12 +214,23 @@ class Inferencer(algorithm.Transformer):
|
|||
|
||||
def visit_Return(self, node):
|
||||
node = self.generic_visit(node)
|
||||
def makenotes(printer, typea, typeb, loca, locb):
|
||||
return [
|
||||
diagnostic.Diagnostic("note",
|
||||
"function with return type {typea}",
|
||||
{"typea": printer.name(typea)},
|
||||
self.function.name_loc),
|
||||
diagnostic.Diagnostic("note",
|
||||
"a statement returning {typeb}",
|
||||
{"typeb": printer.name(typeb)},
|
||||
node.loc)
|
||||
]
|
||||
if node.value is None:
|
||||
self._unify(self.function.return_type, types.TNone(),
|
||||
self.function.name_loc, node.value.loc, kind="return_type_none")
|
||||
self.function.name_loc, node.loc, makenotes)
|
||||
else:
|
||||
self._unify(self.function.return_type, node.value.type,
|
||||
self.function.name_loc, node.value.loc, kind="return_type")
|
||||
self.function.name_loc, node.value.loc, makenotes)
|
||||
|
||||
def visit_Num(self, node):
|
||||
if isinstance(node.n, int):
|
||||
|
@ -267,9 +265,20 @@ class Inferencer(algorithm.Transformer):
|
|||
node = self.generic_visit(node)
|
||||
node = asttyped.ListT(type=types.TList(),
|
||||
elts=node.elts, ctx=node.ctx, loc=node.loc)
|
||||
def makenotes(printer, typea, typeb, loca, locb):
|
||||
return [
|
||||
diagnostic.Diagnostic("note",
|
||||
"a list of type {typea}",
|
||||
{"typea": printer.name(node.type)},
|
||||
loca),
|
||||
diagnostic.Diagnostic("note",
|
||||
"a list element of type {typeb}",
|
||||
{"typeb": printer.name(typeb)},
|
||||
locb)
|
||||
]
|
||||
for elt in node.elts:
|
||||
self._unify(node.type["elt"], elt.type,
|
||||
node.loc, elt.loc, kind="expects")
|
||||
node.loc, elt.loc, makenotes)
|
||||
return node
|
||||
|
||||
def visit_Subscript(self, node):
|
||||
|
@ -279,7 +288,7 @@ class Inferencer(algorithm.Transformer):
|
|||
loc=node.loc)
|
||||
# TODO: support more than just lists
|
||||
self._unify(types.TList(node.type), node.value.type,
|
||||
node.loc, node.value.loc, kind="expects")
|
||||
node.loc, node.value.loc)
|
||||
return node
|
||||
|
||||
def visit_IfExp(self, node):
|
||||
|
@ -380,7 +389,7 @@ class Printer(algorithm.Visitor):
|
|||
def main():
|
||||
import sys, fileinput, os
|
||||
|
||||
if sys.argv[1] == '+diag':
|
||||
if len(sys.argv) > 1 and sys.argv[1] == '+diag':
|
||||
del sys.argv[1]
|
||||
def process_diagnostic(diag):
|
||||
print("\n".join(diag.render(only_line=True)))
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# RUN: OutputCheck %s --file-to-check=%t
|
||||
|
||||
def a():
|
||||
# CHECK-L: ${LINE:+1}: error: can't declare name 'x' as nonlocal: it is not bound in any outer scope
|
||||
# CHECK-L: ${LINE:+1}: error: cannot declare name 'x' as nonlocal: it is not bound in any outer scope
|
||||
nonlocal x
|
||||
|
||||
x = 1
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
# RUN: %python -m artiq.py2llvm.typing +diag %s >%t
|
||||
# RUN: OutputCheck %s --file-to-check=%t
|
||||
|
||||
# CHECK-L: ${LINE:+2}: error: cannot unify int(width='a) with NoneType
|
||||
# CHECK-L: ${LINE:+1}: note: function with return type int(width='a)
|
||||
def a():
|
||||
return 1
|
||||
# CHECK-L: ${LINE:+1}: note: a statement returning NoneType
|
||||
return
|
||||
|
||||
# CHECK-L: ${LINE:+2}: error: cannot unify int(width='a) with list(elt='b)
|
||||
# CHECK-L: ${LINE:+1}: note: function with return type int(width='a)
|
||||
def b():
|
||||
return 1
|
||||
# CHECK-L: ${LINE:+1}: note: a statement returning list(elt='b)
|
||||
return []
|
|
@ -4,5 +4,10 @@
|
|||
a = 1
|
||||
b = []
|
||||
|
||||
# CHECK-L: ${LINE:+1}: fatal: cannot unify int(width='a) with list(elt='b)
|
||||
# CHECK-L: ${LINE:+1}: error: cannot unify int(width='a) with list(elt='b)
|
||||
a = b
|
||||
|
||||
# CHECK-L: ${LINE:+1}: error: cannot unify int(width='a) with list(elt='b)
|
||||
[1, []]
|
||||
# CHECK-L: note: a list of type list(elt=int(width='a))
|
||||
# CHECK-L: note: a list element of type list(elt='b)
|
||||
|
|
Loading…
Reference in New Issue