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 analysis are already performed
23 redef class ToolContext
24 # Run `APropdef::do_local_var_init` on each propdef
25 var local_var_init_phase
: Phase = new LocalVarInitPhase(self, [flow_phase
])
28 private class LocalVarInitPhase
31 redef fun process_npropdef
(npropdef
) do npropdef
.do_local_var_init
(toolcontext
)
35 # Entry point of the whole local variable initialization verifier
36 fun do_local_var_init
(toolcontext
: ToolContext)
38 var v
= new LocalVarInitVisitor(toolcontext
)
43 private class LocalVarInitVisitor
46 var toolcontext
: ToolContext
48 # Local variables that are possibly unset (ie local variable without an initial value)
49 var maybe_unset_vars
: Set[Variable] = new HashSet[Variable]
51 fun mark_is_unset
(node
: AExpr, variable
: nullable Variable)
53 assert variable
!= null
54 self.maybe_unset_vars
.add
(variable
)
57 fun mark_is_set
(node
: AExpr, variable
: nullable Variable)
59 assert variable
!= null
60 if not maybe_unset_vars
.has
(variable
) then return
62 var flow
= node
.after_flow_context
.as(not null)
63 flow
.set_vars
.add
(variable
)
66 fun check_is_set
(node
: AExpr, variable
: nullable Variable)
68 assert variable
!= null
69 if not maybe_unset_vars
.has
(variable
) then return
71 var flow
= node
.after_flow_context
.as(not null)
72 if not flow
.is_variable_set
(variable
) then
73 self.toolcontext
.error
(node
.hot_location
, "Error: variable '{variable}' is possibly unset.")
74 # Remove the variable to avoid repeating errors
75 self.maybe_unset_vars
.remove
(variable
)
81 n
.accept_local_var_visitor
(self)
85 redef class FlowContext
86 private var set_vars
: Set[Variable] = new HashSet[Variable]
88 private fun is_variable_set
(variable
: Variable): Bool
90 if self.set_vars
.has
(variable
) then return true
91 var previous
= self.previous
92 if previous
.length
== 0 then return false
93 if previous
.length
== 1 then return previous
.first
.is_variable_set
(variable
)
94 for p
in self.previous
do
95 if not p
.is_variable_set
(variable
) then
100 self.set_vars
.add
(variable
)
106 private fun accept_local_var_visitor
(v
: LocalVarInitVisitor) do self.visit_all
(v
)
109 redef class AVardeclExpr
110 redef fun accept_local_var_visitor
(v
)
113 # The variable is unset only if there is no initial value.
115 # Note: loops in initial value are not a problem
118 # var foo = foo + 1 #-> Error during typing: "self.foo" unknown
121 # foo = foo + 1 #-> Error here because 'foo' is possibly unset
122 if self.n_expr
== null then
123 v
.mark_is_unset
(self, self.variable
)
129 redef fun accept_local_var_visitor
(v
)
132 v
.check_is_set
(self, self.variable
)
136 redef class AVarAssignExpr
137 redef fun accept_local_var_visitor
(v
)
140 v
.mark_is_set
(self, self.variable
)
144 redef class AVarReassignExpr
145 redef fun accept_local_var_visitor
(v
)
148 v
.check_is_set
(self, self.variable
)