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