Property definitions

nitc $ ExplainAssertVisitor :: defaultinit
# 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