# Visitor to find and explain asserts
private class ExplainAssertVisitor
super Visitor
# The toolcontext is our entry point to most services
var toolcontext: ToolContext
# The visited module
var mmodule: MModule
# Type of `String` (the generated code does not work without a `String`)
var string_mtype: MType
# Tool to modify the AST
var builder = new ASTBuilder(mmodule) is lazy
redef fun visit(node)
do
# Recursively visit all sub-nodes
node.visit_all(self)
# Only work on asserts
if not node isa AAssertExpr then return
var expr = node.n_expr
# Skip assert on a single boolean var and asserts on false:
# ~~~
# assert false
# # or
# var x = false # Or any boolean expression
# assert x
# ~~~
if expr isa AVarExpr or expr isa AFalseExpr then return
# Build the superstring to explain the assert
var explain_str = new ASuperstringExpr
# Prepare attribute used by visited nodes
self.assert_node = node
self.explain_str = explain_str
expr.accept_explain_assert self
# Done! Store the superstring in the assert's node
if explain_str.n_exprs.not_empty then
node.explain_assert_str = explain_str
end
end
# Visited assert node
var assert_node: AAssertExpr is noinit
# Superstring in construction to explain the `assert_node`
var explain_str: ASuperstringExpr is noinit
# Build an `AStringExpr` containing `value`
#
# Add it to `explain_str` if `auto_add == true`, the default.
fun explain_string(value: String, auto_add: nullable Bool): AStringExpr
do
auto_add = auto_add or else true
var tk = new TString
tk.text = "\"{value}\""
var op = new AStringExpr
op.n_string = tk
op.mtype = string_mtype
op.value = value
op.location = assert_node.location
if auto_add then explain_str.n_exprs.add op
return op
end
# Add the value of `v_expr` to `explain_str` and protect null values
fun explain_expr(v_expr: AExpr)
do
var mtype = v_expr.mtype
if mtype == null then
explain_string "<unexpected error>"
return
end
# Set the expression value aside
var old_parent = v_expr.parent
var expr = v_expr.make_var_read
if old_parent != null then old_parent.validate
# Protect nullable types
if mtype isa MNullType then
explain_string "null"
return
else if mtype isa MNullableType then
var e = new AOrElseExpr
e.n_expr = expr
e.n_expr2 = explain_string("null", false)
e.location = assert_node.location
e.mtype = mmodule.object_type
explain_str.n_exprs.add e
return
end
explain_str.n_exprs.add expr
end
# Add all the arguments in `AExprs` to `explain_str`
fun explain_args(n_args: AExprs)
do
var first = true
for n_arg in n_args.to_a do
if not first then
explain_string ", "
else first = false
explain_expr n_arg
end
end
end
src/frontend/explain_assert.nit:65,1--182,3