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
25 redef class ToolContext
26 var local_var_init_phase
: Phase = new LocalVarInitPhase(self, [flow_phase
])
29 private class LocalVarInitPhase
32 redef fun process_npropdef
(npropdef
) do npropdef
.do_local_var_init
(toolcontext
)
36 # Entry point of the whole local variable initialization verifier
37 fun do_local_var_init
(toolcontext
: ToolContext)
39 var v
= new LocalVarInitVisitor(toolcontext
)
44 private class LocalVarInitVisitor
47 var toolcontext
: ToolContext
49 init(toolcontext
: ToolContext)
51 self.toolcontext
= toolcontext
54 # Local variables that are possibily unset (ie local variable without an initial value)
55 var maybe_unset_vars
: Set[Variable] = new HashSet[Variable]
57 fun mark_is_unset
(node
: AExpr, variable
: nullable Variable)
59 assert variable
!= null
60 self.maybe_unset_vars
.add
(variable
)
63 fun mark_is_set
(node
: AExpr, variable
: nullable Variable)
65 assert variable
!= null
66 if not maybe_unset_vars
.has
(variable
) then return
68 var flow
= node
.after_flow_context
.as(not null)
69 flow
.set_vars
.add
(variable
)
72 fun check_is_set
(node
: AExpr, variable
: nullable Variable)
74 assert variable
!= null
75 if not maybe_unset_vars
.has
(variable
) then return
77 var flow
= node
.after_flow_context
.as(not null)
78 if not flow
.is_variable_set
(variable
) then
79 self.toolcontext
.error
(node
.hot_location
, "Error: variable '{variable}' is possibly unset.")
80 # Remove the variable to avoid repetting errors
81 self.maybe_unset_vars
.remove
(variable
)
87 if n
!= null then n
.accept_local_var_visitor
(self)
91 redef class FlowContext
92 private var set_vars
: Set[Variable] = new HashSet[Variable]
94 private fun is_variable_set
(variable
: Variable): Bool
96 if self.set_vars
.has
(variable
) then return true
97 var previous
= self.previous
98 if previous
.length
== 0 then return false
99 if previous
.length
== 1 then return previous
.first
.is_variable_set
(variable
)
100 for p
in self.previous
do
101 if not p
.is_variable_set
(variable
) then
106 self.set_vars
.add
(variable
)
112 private fun accept_local_var_visitor
(v
: LocalVarInitVisitor) do self.visit_all
(v
)
115 redef class AVardeclExpr
116 redef fun accept_local_var_visitor
(v
)
119 # The variable is unset only if there is no initial value.
121 # Note: loops in inital value are not a problem
124 # var foo = foo + 1 #-> Error during typing: "self.foo" unknown
127 # foo = foo + 1 #-> Error here because 'foo' is possibly unset
128 if self.n_expr
== null then
129 v
.mark_is_unset
(self, self.variable
)
135 redef fun accept_local_var_visitor
(v
)
138 v
.check_is_set
(self, self.variable
)
142 redef class AVarAssignExpr
143 redef fun accept_local_var_visitor
(v
)
146 v
.mark_is_set
(self, self.variable
)
150 redef class AVarReassignExpr
151 redef fun accept_local_var_visitor
(v
)
154 v
.check_is_set
(self, self.variable
)