# See the License for the specific language governing permissions and
# limitations under the License.
-# Identification and scping of local variables and labels.
+# Identification and scoping of local variables and labels.
module scope
import phase
# Alias of `name`
redef fun to_s do return self.name
+
+ # The declaration of the variable, if any
+ var location: nullable Location = null
+
+ # Is the local variable not read and need a warning?
+ var warn_unread = false is writable
end
# Mark where break and continue will branch.
# The name of the label (unless the mark is an anonymous loop mark)
var name: nullable String
- # Is the mark atached to a loop (loop, while, for)
+ # Is the mark attached to a loop (loop, while, for)
# Such a mark is a candidate to a labelless 'continue' or 'break'
var for_loop: Bool
# Each 'continue' attached to the mark
- var continues: Array[AContinueExpr] = new Array[AContinueExpr]
+ var continues = new Array[AContinueExpr]
# Each 'break' attached to the mark
- var breaks: Array[ABreakExpr] = new Array[ABreakExpr]
+ var breaks = new Array[ABreakExpr]
end
# Visit a npropdef and:
# The tool context used to display errors
var toolcontext: ToolContext
- var selfvariable: Variable = new Variable("self")
+ var selfvariable = new Variable("self")
init(toolcontext: ToolContext)
do
end
# All stacked scope. `scopes.first` is the current scope
- private var scopes: List[Scope] = new List[Scope]
+ private 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
- # Regiter a local variable.
+ # 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
return false
end
scopes.first.variables[name] = variable
+ variable.location = node.location
return true
end
# Enter in a statement block `node` as inside a new scope.
# The block can be optionally attached to an `escapemark`.
- private fun enter_visit_block(node: nullable AExpr, escapemark: nullable 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)
- scopes.shift
+ shift_scope
end
# Look for a label `name`.
- # Return nulll if no such a label is found.
- private fun search_label(name: String): nullable EscapeMark
+ # 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
# Create a new escape mark (possibly with a label)
# Display an error on toolcontext if a label with the same name is masked.
- private fun make_escape_mark(nlabel: nullable ALabel, for_loop: Bool): EscapeMark
+ fun make_escape_mark(nlabel: nullable ALabel, for_loop: Bool): EscapeMark
do
var name: nullable String
if nlabel != null then
end
# Look for an escape mark optionally associated with a label.
- # If a label is given, the the escapemark of this label is returned.
+ # 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 nulll if no such a label is found.
- private fun get_escapemark(node: ANode, nlabel: nullable ALabel): nullable EscapeMark
+ # 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
return res
end
end
- self.error(node, "Syntax Error: 'break' statment outside block.")
+ self.error(node, "Syntax Error: 'break' statement outside block.")
return null
end
end
# Display an error
- private fun error(node: ANode, message: String)
+ fun error(node: ANode, message: String)
do
self.toolcontext.error(node.hot_location, message)
end
end
private class Scope
- var variables: HashMap[String, Variable] = new HashMap[String, Variable]
+ var variables = new HashMap[String, Variable]
var escapemark: nullable EscapeMark = null
do
var v = new ScopeVisitor(toolcontext)
v.enter_visit(self)
+ v.shift_scope
end
end
var nid = self.n_id
var variable = new Variable(nid.text)
v.register_variable(nid, variable)
+ variable.warn_unread = true # wait for some read mark.
self.variable = variable
end
end
redef class ASelfExpr
- # The variable associated with the self reciever
+ # The variable associated with the self receiver
var variable: nullable Variable
redef fun accept_scope_visitor(v)
do
end
end
-redef class AContinueExpr
- # The escape mark associated with the continue
+redef class AEscapeExpr
+ # The escape mark associated with the break/continue
var escapemark: nullable EscapeMark
+end
+
+redef class AContinueExpr
redef fun accept_scope_visitor(v)
do
super
end
redef class ABreakExpr
- # The escape mark associated with the break
- var escapemark: nullable EscapeMark
redef fun accept_scope_visitor(v)
do
super
self.escapemark = escapemark
v.enter_visit_block(n_block, escapemark)
- v.scopes.shift
+ v.shift_scope
end
end
super
end
- # Create a variable acces corresponding to the call form
+ # Create a variable access corresponding to the call form
private fun variable_create(variable: Variable): AVarFormExpr is abstract
end
redef class ACallExpr
redef fun variable_create(variable)
do
+ variable.warn_unread = false
return new AVarExpr.init_avarexpr(n_id)
end
end
redef class ACallReassignExpr
redef fun variable_create(variable)
do
+ variable.warn_unread = false
return new AVarReassignExpr.init_avarreassignexpr(n_id, n_assign_op, n_value)
end
end