Bug 158 -- raise error if cdef variable declared after it's used.
authorKurt Smith <kwsmith1@wisc.edu>
Tue, 17 Mar 2009 05:02:46 +0000 (00:02 -0500)
committerKurt Smith <kwsmith1@wisc.edu>
Tue, 17 Mar 2009 05:02:46 +0000 (00:02 -0500)
Cython/Compiler/ParseTreeTransforms.py
tests/errors/declareafteruse_T158.pyx [new file with mode: 0644]

index 3f92d738c9d09ac1cf3429dbc7b21e7948aa73d4..d9f37c07d9df90ce369e1504b595a0688c4d00b3 100644 (file)
@@ -658,6 +658,8 @@ class DecoratorTransform(CythonTransform, SkipDeclarations):
             rhs = decorator_result)
         return [func_node, reassignment]
 
+
+ERR_DEC_AFTER = "cdef variable '%s' declared after it's used."
 class AnalyseDeclarationsTransform(CythonTransform):
 
     basic_property = TreeFragment(u"""
@@ -670,14 +672,23 @@ property NAME:
 
     def __call__(self, root):
         self.env_stack = [root.scope]
+        # needed to determine if a cdef var is declared after it's used.
+        self.local_scope_stack = []
         return super(AnalyseDeclarationsTransform, self).__call__(root)        
     
+    def visit_NameNode(self, node):
+        self.local_scope_stack[-1].add(node.name)
+        return node
+
     def visit_ModuleNode(self, node):
+        self.local_scope_stack.append(set())
         node.analyse_declarations(self.env_stack[-1])
         self.visitchildren(node)
+        self.local_scope_stack.pop()
         return node
         
     def visit_FuncDefNode(self, node):
+        self.local_scope_stack.append(set())
         lenv = node.create_local_scope(self.env_stack[-1])
         node.body.analyse_control_flow(lenv) # this will be totally refactored
         node.declare_arguments(lenv)
@@ -692,6 +703,7 @@ property NAME:
         self.env_stack.append(lenv)
         self.visitchildren(node)
         self.env_stack.pop()
+        self.local_scope_stack.pop()
         return node
     
     # Some nodes are no longer needed after declaration
@@ -699,6 +711,8 @@ property NAME:
     # on these nodes in a seperate recursive process from the
     # enclosing function or module, so we can simply drop them.
     def visit_CDeclaratorNode(self, node):
+        # necessary to ensure that all CNameDeclaratorNodes are visited.
+        self.visitchildren(node)
         return node
     
     def visit_CTypeDefNode(self, node):
@@ -713,7 +727,18 @@ property NAME:
     def visit_CStructOrUnionDefNode(self, node):
         return None
 
+    def visit_CNameDeclaratorNode(self, node):
+        if node.name in self.local_scope_stack[-1]:
+            # cdef variable declared after it's used.
+            error(node.pos, ERR_DEC_AFTER % node.name)
+        self.visitchildren(node)
+        return node
+
     def visit_CVarDefNode(self, node):
+
+        # to ensure all CNameDeclaratorNodes are visited.
+        self.visitchildren(node)
+
         if node.need_properties:
             # cdef public attributes may need type testing on 
             # assignment, so we create a property accesss
diff --git a/tests/errors/declareafteruse_T158.pyx b/tests/errors/declareafteruse_T158.pyx
new file mode 100644 (file)
index 0000000..364c942
--- /dev/null
@@ -0,0 +1,65 @@
+def mult_decl_test():
+    print "%s" % vv
+    print "%s" % s
+    cdef str s, vv = "Test"
+
+def def_test():
+    cdef int j = 10
+    i[0] = j
+    cdef int *i = NULL # pointer variables are special case
+
+cdef cdef_test():
+    cdef int j = 10
+    i[0] = j
+    print "%d" % i[0]
+    cdef int *i = NULL
+
+cpdef cpdef_test():
+    cdef int j = 10
+    i[0] = j
+    print "%d" % i[0]
+    cdef int *i = NULL
+
+s.upper()
+cdef str s = "Test"
+
+class Foo(object):
+    def bar(self, x, y):
+        cdef unsigned long w = 20
+        z = w + t
+        cdef int t = 10
+
+cdef class Foo2(object):
+    print '%s' % r # check error inside class scope
+    cdef str r
+    def bar(self, x, y):
+        cdef unsigned long w = 20
+        self.r = c'r'
+        print self.r
+        z = w + g(t)
+        cdef int t = 10
+
+def g(x):
+    return x
+
+cdef int d = 20
+baz[0] = d
+cdef int *baz
+
+print var[0][0]
+cdef unsigned long long var[100][100]
+
+
+_ERRORS = u"""
+4:13: cdef variable 's' declared after it's used.
+4:16: cdef variable 'vv' declared after it's used.
+9:14: cdef variable 'i' declared after it's used.
+15:14: cdef variable 'i' declared after it's used.
+21:14: cdef variable 'i' declared after it's used.
+24:9: cdef variable 's' declared after it's used.
+30:17: cdef variable 't' declared after it's used.
+34:13: cdef variable 'r' declared after it's used.
+40:17: cdef variable 't' declared after it's used.
+47:10: cdef variable 'baz' declared after it's used.
+50:24: cdef variable 'var' declared after it's used.
+"""