1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2012 Jean Privat <jean@pryen.org>
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 # Verify that local variables are initialized before their usage
18 # Require that the scope and the flow analaysis are already performed
23 redef class ToolContext
24 var local_var_init_phase
: Phase = new LocalVarInitPhase(self, [flow_phase
])
27 private class LocalVarInitPhase
30 redef fun process_npropdef
(npropdef
) do npropdef
.do_local_var_init
(toolcontext
)
34 # Entry point of the whole local variable initialization verifier
35 fun do_local_var_init
(toolcontext
: ToolContext)
37 var v
= new LocalVarInitVisitor(toolcontext
)
42 private class LocalVarInitVisitor
45 var toolcontext
: ToolContext
47 init(toolcontext
: ToolContext)
49 self.toolcontext
= toolcontext
52 # Local variables that are possibily unset (ie local variable without an initial value)
53 var maybe_unset_vars
: Set[Variable] = new HashSet[Variable]
55 fun mark_is_unset
(node
: AExpr, variable
: nullable Variable)
57 assert variable
!= null
58 self.maybe_unset_vars
.add
(variable
)
61 fun mark_is_set
(node
: AExpr, variable
: nullable Variable)
63 assert variable
!= null
64 if not maybe_unset_vars
.has
(variable
) then return
66 var flow
= node
.after_flow_context
.as(not null)
67 flow
.set_vars
.add
(variable
)
70 fun check_is_set
(node
: AExpr, variable
: nullable Variable)
72 assert variable
!= null
73 if not maybe_unset_vars
.has
(variable
) then return
75 var flow
= node
.after_flow_context
.as(not null)
76 if not flow
.is_variable_set
(variable
) then
77 self.toolcontext
.error
(node
.hot_location
, "Error: variable '{variable}' is possibly unset.")
78 # Remove the variable to avoid repetting errors
79 self.maybe_unset_vars
.remove
(variable
)
85 n
.accept_local_var_visitor
(self)
89 redef class FlowContext
90 private var set_vars
: Set[Variable] = new HashSet[Variable]
92 private fun is_variable_set
(variable
: Variable): Bool
94 if self.set_vars
.has
(variable
) then return true
95 var previous
= self.previous
96 if previous
.length
== 0 then return false
97 if previous
.length
== 1 then return previous
.first
.is_variable_set
(variable
)
98 for p
in self.previous
do
99 if not p
.is_variable_set
(variable
) then
104 self.set_vars
.add
(variable
)
110 private fun accept_local_var_visitor
(v
: LocalVarInitVisitor) do self.visit_all
(v
)
113 redef class AVardeclExpr
114 redef fun accept_local_var_visitor
(v
)
117 # The variable is unset only if there is no initial value.
119 # Note: loops in inital value are not a problem
122 # var foo = foo + 1 #-> Error during typing: "self.foo" unknown
125 # foo = foo + 1 #-> Error here because 'foo' is possibly unset
126 if self.n_expr
== null then
127 v
.mark_is_unset
(self, self.variable
)
133 redef fun accept_local_var_visitor
(v
)
136 v
.check_is_set
(self, self.variable
)
140 redef class AVarAssignExpr
141 redef fun accept_local_var_visitor
(v
)
144 v
.mark_is_set
(self, self.variable
)
148 redef class AVarReassignExpr
149 redef fun accept_local_var_visitor
(v
)
152 v
.check_is_set
(self, self.variable
)