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
|
break
|
||||||
if not found:
|
if not found:
|
||||||
diag = diagnostic.Diagnostic("error",
|
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},
|
{"name": name},
|
||||||
loc, [node.keyword_loc])
|
loc, [node.keyword_loc])
|
||||||
self.engine.process(diag)
|
self.engine.process(diag)
|
||||||
|
@ -132,51 +132,38 @@ class Inferencer(algorithm.Transformer):
|
||||||
self.env_stack = []
|
self.env_stack = []
|
||||||
self.function = None # currently visited function
|
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:
|
try:
|
||||||
typea.unify(typeb)
|
typea.unify(typeb)
|
||||||
except types.UnificationError as e:
|
except types.UnificationError as e:
|
||||||
printer = types.TypePrinter()
|
printer = types.TypePrinter()
|
||||||
|
|
||||||
if kind == "expects":
|
if makenotes:
|
||||||
note1 = diagnostic.Diagnostic("note",
|
notes = makenotes(printer, typea, typeb, loca, locb)
|
||||||
"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)
|
|
||||||
else:
|
else:
|
||||||
note1 = diagnostic.Diagnostic("note",
|
notes = [
|
||||||
|
diagnostic.Diagnostic("note",
|
||||||
"expression of type {typea}",
|
"expression of type {typea}",
|
||||||
{"typea": printer.name(typea)},
|
{"typea": printer.name(typea)},
|
||||||
loca)
|
loca),
|
||||||
|
diagnostic.Diagnostic("note",
|
||||||
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}",
|
"expression of type {typeb}",
|
||||||
{"typeb": printer.name(typeb)},
|
{"typeb": printer.name(typeb)},
|
||||||
locb)
|
locb)
|
||||||
|
]
|
||||||
|
|
||||||
highlights = [locb] if locb else []
|
highlights = [locb] if locb else []
|
||||||
if e.typea.find() == typea.find() and e.typeb.find() == typeb.find():
|
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}",
|
"cannot unify {typea} with {typeb}",
|
||||||
{"typea": printer.name(typea), "typeb": printer.name(typeb)},
|
{"typea": printer.name(typea), "typeb": printer.name(typeb)},
|
||||||
loca, highlights, notes=[note1, note2])
|
loca, highlights, notes)
|
||||||
else: # give more detail
|
else: # give more detail
|
||||||
diag = diagnostic.Diagnostic("fatal",
|
diag = diagnostic.Diagnostic("error",
|
||||||
"cannot unify {typea} with {typeb}: {fraga} is incompatible with {fragb}",
|
"cannot unify {typea} with {typeb}: {fraga} is incompatible with {fragb}",
|
||||||
{"typea": printer.name(typea), "typeb": printer.name(typeb),
|
{"typea": printer.name(typea), "typeb": printer.name(typeb),
|
||||||
"fraga": printer.name(e.typea), "fragb": printer.name(e.typeb)},
|
"fraga": printer.name(e.typea), "fragb": printer.name(e.typeb)},
|
||||||
loca, highlights, notes=[note1, note2])
|
loca, highlights, notes)
|
||||||
self.engine.process(diag)
|
self.engine.process(diag)
|
||||||
|
|
||||||
def _find_name(self, name, loc):
|
def _find_name(self, name, loc):
|
||||||
|
@ -227,12 +214,23 @@ class Inferencer(algorithm.Transformer):
|
||||||
|
|
||||||
def visit_Return(self, node):
|
def visit_Return(self, node):
|
||||||
node = self.generic_visit(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:
|
if node.value is None:
|
||||||
self._unify(self.function.return_type, types.TNone(),
|
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:
|
else:
|
||||||
self._unify(self.function.return_type, node.value.type,
|
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):
|
def visit_Num(self, node):
|
||||||
if isinstance(node.n, int):
|
if isinstance(node.n, int):
|
||||||
|
@ -267,9 +265,20 @@ class Inferencer(algorithm.Transformer):
|
||||||
node = self.generic_visit(node)
|
node = self.generic_visit(node)
|
||||||
node = asttyped.ListT(type=types.TList(),
|
node = asttyped.ListT(type=types.TList(),
|
||||||
elts=node.elts, ctx=node.ctx, loc=node.loc)
|
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:
|
for elt in node.elts:
|
||||||
self._unify(node.type["elt"], elt.type,
|
self._unify(node.type["elt"], elt.type,
|
||||||
node.loc, elt.loc, kind="expects")
|
node.loc, elt.loc, makenotes)
|
||||||
return node
|
return node
|
||||||
|
|
||||||
def visit_Subscript(self, node):
|
def visit_Subscript(self, node):
|
||||||
|
@ -279,7 +288,7 @@ class Inferencer(algorithm.Transformer):
|
||||||
loc=node.loc)
|
loc=node.loc)
|
||||||
# TODO: support more than just lists
|
# TODO: support more than just lists
|
||||||
self._unify(types.TList(node.type), node.value.type,
|
self._unify(types.TList(node.type), node.value.type,
|
||||||
node.loc, node.value.loc, kind="expects")
|
node.loc, node.value.loc)
|
||||||
return node
|
return node
|
||||||
|
|
||||||
def visit_IfExp(self, node):
|
def visit_IfExp(self, node):
|
||||||
|
@ -380,7 +389,7 @@ class Printer(algorithm.Visitor):
|
||||||
def main():
|
def main():
|
||||||
import sys, fileinput, os
|
import sys, fileinput, os
|
||||||
|
|
||||||
if sys.argv[1] == '+diag':
|
if len(sys.argv) > 1 and sys.argv[1] == '+diag':
|
||||||
del sys.argv[1]
|
del sys.argv[1]
|
||||||
def process_diagnostic(diag):
|
def process_diagnostic(diag):
|
||||||
print("\n".join(diag.render(only_line=True)))
|
print("\n".join(diag.render(only_line=True)))
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# RUN: OutputCheck %s --file-to-check=%t
|
# RUN: OutputCheck %s --file-to-check=%t
|
||||||
|
|
||||||
def a():
|
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
|
nonlocal x
|
||||||
|
|
||||||
x = 1
|
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
|
a = 1
|
||||||
b = []
|
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
|
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