Property definitions

nitc $ ScopeVisitor :: defaultinit
# Visit a npropdef and:
#  * Identify variables and labels
#  * Associate each break and continue to its escapemark
#  * Transform `ACallFormExpr` that access a variable into `AVarFormExpr`
# FIXME: Should the class be private?
private class ScopeVisitor
	super Visitor

	# The tool context used to display errors
	var toolcontext: ToolContext

	# The analysed property
	var propdef: APropdef

	var selfvariable = new Variable("self")

	init
	do
		scopes.add(new Scope)
	end

	# All stacked scope. `scopes.first` is the current scope
	var scopes = new List[Scope]

	# Shift and check the last scope
	fun shift_scope
	do
		assert not scopes.is_empty
		var scope = scopes.shift
		for v in scope.variables.values do
			if v.warn_unread then
				toolcontext.advice(v.location, "unread-variable", "Warning: local variable {v.name} is never read.")
			end
		end
	end

	# Register a local variable.
	# Display an error on toolcontext if a variable with the same name is masked.
	fun register_variable(node: ANode, variable: Variable): Bool
	do
		var name = variable.name
		var found = search_variable(name)
		if found != null then
			self.error(node, "Error: a variable named `{name}` already exists.")
			return false
		end
		scopes.first.variables[name] = variable
		variable.location = node.location
		return true
	end

	# Look for a variable named `name`.
	# Return null if no such a variable is found.
	fun search_variable(name: String): nullable Variable
	do
		for scope in scopes do
			var res = scope.get_variable(name)
			if res != null then
				return res
			end
		end
		return null
	end

	redef fun visit(n)
	do
		n.accept_scope_visitor(self)
	end

	# Enter in a statement block `node` as inside a new scope.
	# The block can be optionally attached to an `escapemark`.
	fun enter_visit_block(node: nullable AExpr, escapemark: nullable EscapeMark)
	do
		if node == null then return
		var scope = new Scope
		scope.escapemark = escapemark
		scopes.unshift(scope)
		enter_visit(node)
		shift_scope
	end

	# Look for a label `name`.
	# Return null if no such a label is found.
	fun search_label(name: String): nullable EscapeMark
	do
		for scope in scopes do
			var res = scope.escapemark
			if res != null and res.name == name then
				return res
			end
		end
		return null
	end

	# Create a new escape mark (possibly with a label)
	# Display an error on toolcontext if a label with the same name is masked.
	fun make_escape_mark(nlabel: nullable ALabel, for_loop: Bool): EscapeMark
	do
		var name: nullable String
		if nlabel != null then
			var nid = nlabel.n_id
			if nid == null then
				var res = search_label("")
				if res != null then
					self.error(nlabel, "Syntax Error: anonymous label already defined.")
				end
				name = ""
			else
				name = nid.text
				var found = self.search_label(name)
				if found != null then
					self.error(nlabel, "Syntax Error: label `{name}` already defined.")
				end
			end
		else
			name = null
		end
		var res = new EscapeMark(name)
		if for_loop then res.continue_mark = new EscapeMark(name)
		return res
	end

	# Look for an escape mark optionally associated with a label.
	# If a label is given, the escapemark of this label is returned.
	# If there is no label, the nearest escapemark that is `for loop` is returned.
	# If there is no valid escapemark, then an error is displayed ans null is returned.
	# Return null if no such a label is found.
	fun get_escapemark(node: ANode, nlabel: nullable ALabel): nullable EscapeMark
	do
		if nlabel != null then
			var nid = nlabel.n_id
			if nid == null then
				var res = search_label("")
				if res == null then
					self.error(nlabel, "Syntax Error: invalid anonymous label.")
					node.is_broken = true
					return null
				end
				return res
			end
			var name = nid.text
			var res = search_label(name)
			if res == null then
				self.error(nlabel, "Syntax Error: invalid label `{name}`.")
				node.is_broken = true
				return null
			end
			return res
		else
			for scope in scopes do
				var res = scope.escapemark
				if res != null then
					return res
				end
			end
			self.error(node, "Syntax Error: `break` statement outside block.")
			return null
		end
	end

	# Display an error
	fun error(node: ANode, message: String)
	do
		self.toolcontext.error(node.hot_location, message)
		node.is_broken = true
	end
end
src/semantize/scope.nit:63,1--229,3