def _isScopedVar(self, idString, node, fileId):

        def findScopeNodeAndRoot(node):
            node1 = node
            sNode = None
            rNode = None
            while True:
                if not sNode and node1.type in ["function", "catch"]:
                    sNode = node1
                if node1.hasParent():
                    node1 = node1.parent
                else:  # we're at the root
                    if not sNode:
                        sNode = node1
                    rNode = node1
                    break
            return sNode, rNode

        # check composite id a.b.c, check only first part
        dotIdx = idString.find('.')
        if dotIdx > -1:
            idString = idString[:dotIdx]
        scopeNode, rootNode  = findScopeNodeAndRoot(node)  # find the node of the enclosing scope (function - catch - global)
        script = Script(rootNode, fileId)
        if scopeNode == rootNode:
            fcnScope = script.getGlobalScope()
        else:
            fcnScope = script.getScope(scopeNode)
        varDef = script.getVariableDefinition(idString, fcnScope)
        if varDef:
            return True
        return False
Example #2
0
    def __init__(self, filename, logger=None):
        self.filename = filename
        content = filetool.read(filename)

        self.tree = treegenerator.createSyntaxTree(
            tokenizer.parseStream(content))
        self.script = Script(self.tree, self.filename)
        if not logger:
            self.logger = ConsoleLogger()
        else:
            self.logger = logger
Example #3
0
def search(node):
    def updateOccurences(var, newname):
        # Replace variable definition
        for node in var.nodes:
            update(node, newname)

        # Replace variable references
        for varUse in var.uses:  # varUse is a VariableUse object
            update(varUse.node, newname)

    def getAllVarOccurrences(script):
        # Collect the set of all used and declared ('var' + params) variables
        varset = set(())
        for scope in script.iterScopes():
            varset.update(
                (x.name
                 for x in scope.arguments + scope.variables + scope.uses))
        return varset

    global counter
    counter = 0  # reset repl name generation

    script = Script(node)
    checkSet = getAllVarOccurrences(
        script)  # set of var names already in use, to check new names against

    # loop through declared vars of scopes
    for scope in script.iterScopes():
        allvars = scope.arguments + scope.variables
        for var in allvars:

            if var.name in reservedWords or len(var.name) < 2:
                continue

            # get replacement name
            newname = mapper(var.name, checkSet)

            # update all occurrences in scope
            updateOccurences(var, newname)

            # if var is param, patch local vars of same name in one go
            if (var in scope.arguments):
                # get declared vars of same name
                lvars = [x for x in scope.variables if x.name == var.name]
                for lvar in lvars:
                    updateOccurences(lvar, newname)
                    # don't re-process
                    allvars.remove(lvar)
def search(node):

    def updateOccurences(var, newname):
        # Replace variable definition
        for node in var.nodes:
            update(node, newname)

        # Replace variable references
        for varUse in var.uses:  # varUse is a VariableUse object
            update(varUse.node, newname)

    def getAllVarOccurrences(script):
        # Collect the set of all used and declared ('var' + params) variables
        varset = set(())
        for scope in script.iterScopes():
            varset.update((x.name for x in scope.arguments + scope.variables + scope.uses))
        return varset

    global counter
    counter = 0 # reset repl name generation

    script   = Script(node)
    checkSet = getAllVarOccurrences(script)  # set of var names already in use, to check new names against

    # loop through declared vars of scopes
    for scope in script.iterScopes():
        allvars = scope.arguments + scope.variables

        for var in allvars:
            if var.name in reservedWords or len(var.name)<2:
                continue

            # get replacement name
            newname = mapper(var.name, checkSet)

            # update all occurrences in scope
            updateOccurences(var, newname)

            # if var is param, patch local vars of same name in one go
            if (var in scope.arguments):
                # get declared vars of same name
                lvars = [x for x in scope.variables if x.name == var.name]
                for lvar in lvars:
                    updateOccurences(lvar, newname)
                    # don't re-process
                    allvars.remove(lvar)
Example #5
0
    def __init__(self, filename, logger=None):
        self.filename = filename
        content = filetool.read(filename)

        self.tree = treegenerator.createSyntaxTree(tokenizer.parseStream(content))
        self.script = Script(self.tree, self.filename)
        if not logger:
            self.logger = ConsoleLogger()
        else:
            self.logger = logger
Example #6
0
 def getScript(node, fileId, ):
     # TODO: checking the root nodes is a fix, as they sometimes differ (prob. caching)
     # -- looking up nodes in a Script() uses object identity for comparison; sometimes, the
     #    tree _analyzeClassDepsNode works on and the tree Script is built from are not the
     #    same in memory, e.g. when the tree is re-read from disk; then those comparisons
     #    fail (although the nodes are semantically the same); hence we have to
     #    re-calculate the Script (which is expensive!) when the root node object changes;
     #    using __memo allows at least to re-use the existing script when a class is worked
     #    on and this method is called successively for the same tree.
     rootNode = node.getRoot()
     #if _memo1_[0] == fileId: # replace with '_memo1_[0] == rootNode', to make it more robust, but slightly less performant
     if _memo1_[0] == rootNode:
         script = _memo1_[1]
     else:
         # TODO: disentagle use of ecmascript.frontend.Script and generator.code.Script
         script = Script(rootNode, fileId)
         _memo1_[0], _memo1_[1] = rootNode, script
     return script
Example #7
0
class Lint:
    def __init__(self, filename, logger=None):
        self.filename = filename
        content = filetool.read(filename)

        self.tree = treegenerator.createSyntaxTree(tokenizer.parseStream(content))
        self.script = Script(self.tree, self.filename)
        if not logger:
            self.logger = ConsoleLogger()
        else:
            self.logger = logger


    def log(self, node, msg):
        (row, column) = treeutil.getLineAndColumnFromSyntaxItem(node)
        self.logger.log(self.filename, row, column, msg)


    def checkRequiredBlocks(self):
        for node in treeutil.nodeIterator(self.tree, "loop"):
            block = treeutil.selectNode(node, "statement/block")
            if not block:
                self.log(node, "The statement of loops and conditions must be enclosed by a block in braces '{}'")


    def checkMaps(self):
        for node in treeutil.nodeIterator(self.tree, "map"):
            knownkeys = {}
            if node.hasChildren():
                for child in node.children:
                    if child.type == "keyvalue":
                        key = child.get("key")
                        if knownkeys.has_key(key):
                            self.log(child, "Map key '%s' redefined." % key)
                        else:
                            knownkeys[key] = child


    def checkFields(self):
        define = treeutil.findQxDefine(self.tree)
        if not define:
            return

        classMapNode = treeutil.selectNode(define, "params/2")
        if classMapNode is None:
            return

        classMap = treeutil.mapNodeToMap(classMapNode)
        if not classMap.has_key("members"):
            return

        members = treeutil.mapNodeToMap(classMap["members"].children[0])
        restricted = [key for key in members if key.startswith("_")]

        assignNodes = [node for node in treeutil.nodeIterator(classMap["members"], "assignment")]
        if classMap.has_key("construct"):
            for node in treeutil.nodeIterator(classMap["construct"], "assignment"):
                assignNodes.append(node)

        for node in assignNodes:
            this = treeutil.selectNode(node, "left/variable/identifier[1]/@name")
            if this != "this":
                continue

            field = treeutil.selectNode(node, "left/variable/identifier[2]/@name")
            if field is None:
                continue

            if field[0] != "_":
                continue
            elif field[1] == "_":
                prot = "private"
            else:
                prot = "protected"

            if prot == "protected":
                self.log(node, "Protected data field '%s'. Protected fields are deprecated. Better use private fields in combination with getter and setter methods." % field)
            elif not field in restricted:
                self.log(node, "Implicit declaration of %s field '%s'. You should list this field in the members section." % (prot, field))



    def checkUnusedVariables(self):
        for scope in self.script.iterScopes():
            if scope.type != Scope.EXCEPTION:
                for var in scope.variables:
                    if len(var.uses) == 0:
                        for node in var.nodes:
                            self.log(node, "Unused identifier '%s'" % var.name)


    def checkMultiDefinedVariables(self):
        for scope in self.script.iterScopes():
            if scope.type != Scope.EXCEPTION:
                for var in scope.variables:
                    if len(var.nodes) > 1:
                        for node in var.nodes:
                            self.log(node, "Multiply declared identifier '%s'" % var.name)


    DEPRECATED_IDENTIFIER = set([
        "alert",
        "confirm",
        "debugger",
        "eval"
    ])

    def isBadGlobal(self, identifier):
        return identifier in Lint.DEPRECATED_IDENTIFIER

    KNOWN_IDENTIFIER = set(lang.GLOBALS)

    def isGoodGlobal(self, identifier):
        return identifier in Lint.KNOWN_IDENTIFIER

    def checkUndefinedVariables(self, globals):
        
        # check whether this is a qooxdoo class and extract the top level namespace
        define = treeutil.findQxDefine(self.tree)
        if define:
            className = treeutil.selectNode(define, "params/1").get("value")
            globals.append(className.split(".")[0])        
        
        globalScope = self.script.getGlobalScope()
        for scope in self.script.iterScopes():
            for use in scope.uses:

                if use.name in globals:
                    continue

                if not use.definition:
                    if self.isBadGlobal(use.name):
                        self.log(use.node, "Use of deprecated global identifier '%s'" % use.name)
                    elif not self.isGoodGlobal(use.name):
                        self.log(use.node, "Use of undefined or global identifier '%s'" % use.name)

                elif use.definition.scope == globalScope:
                    self.log(use.node, "Use of global identifier '%s'" % use.name)
Example #8
0
class Lint:
    def __init__(self, filename, logger=None):
        self.filename = filename
        content = filetool.read(filename)

        self.tree = treegenerator.createSyntaxTree(tokenizer.parseStream(content))
        self.script = Script(self.tree, self.filename)
        if not logger:
            self.logger = ConsoleLogger()
        else:
            self.logger = logger
            
            
    def log(self, node, msg):
        (row, column) = treeutil.getLineAndColumnFromSyntaxItem(node)
        self.logger.log(self.filename, row, column, msg)


    def checkRequiredBlocks(self):
        for node in treeutil.nodeIterator(self.tree, "loop"):
            block = treeutil.selectNode(node, "statement/block")
            if not block:
                self.log(node, "The statement of loops and conditions should be enclosed by a block in braces '{}'")
        for node in treeutil.nodeIterator(self.tree, "elseStatement"):
            block = treeutil.selectNode(node, "block")
            if not block:
                block = treeutil.selectNode(node, "loop[@loopType='IF']")
            if not block:
                self.log(node, "The statement of loops and conditions should be enclosed by a block in braces '{}'")


    def checkMaps(self):
        for node in treeutil.nodeIterator(self.tree, "map"):
            knownkeys = {}
            if node.hasChildren():
                for child in node.children:
                    if child.type == "keyvalue":
                        key = child.get("key")
                        if key in knownkeys:
                            self.log(child, "Map key '%s' redefined." % key)
                        else:
                            knownkeys[key] = child


    def checkFields(self):

        def getVariables():
            # get all variable nodes from 'members' and (pot.) 'construct'
            variables = []
            if "members" in classMap:
                variables.extend([node for node in treeutil.nodeIterator(classMap["members"], ["variable"])])
            if "construct" in classMap:
                variables.extend([node for node in treeutil.nodeIterator(classMap["construct"], ["variable"])])
            return variables

        def checkPrivate(allVars):

            privateElement = re.compile(r'\b__')

            def findPrivate(allVars):
                variables = []
                for node in allVars:
                    fullName, isComplete = treeutil.assembleVariable(node)
                    if privateElement.search(fullName):
                        variables.append(node)
                return variables

            def isLocalPrivate(var, fullName):
                allIdentifier = fullName.split('.')
                first = second = None
                if len(allIdentifier) > 0:
                    first  = allIdentifier[0]
                if len(allIdentifier) > 1:
                    second = allIdentifier[1]
                return (first and
                        (first == "this" or first == "that") and 
                        second and
                        privateElement.match(second))
                
            variables = findPrivate(allVars)
            for var in variables:
                fullName = treeutil.assembleVariable(var)[0]
                if isLocalPrivate(var, fullName) and fullName.split('.')[1] not in restricted: # local privates are ok, as long as they are declared
                    self.log(var, "Undeclared private data field '%s'. You should list this field in the members section." % fullName)
            return

        def checkProtected(allVars):

            protectedElement = re.compile(r'\b_[^_]')

            def findProtected(allVars):
                variables = []
                for node in allVars:
                    # only check protected in lval position
                    if (node.hasParent() and node.parent.type == "left" and
                        node.parent.hasParent() and node.parent.parent.type == "assignment" and
                        protectedElement.search(treeutil.assembleVariable(node)[0])):
                        variables.append(node)
                return variables

            def protectedIsLastVarChild(var):
                lastChild  = var.getLastChild(ignoreComments=True)  # like "this.a.b" -> b
                if lastChild.type != "identifier":  # rules out this.a._prot[0] which isn't a call anyway
                    return False
                name = treeutil.selectNode(lastChild, "@name")
                if name and protectedElement.match(name):
                    return True
                else:
                    return False

            variables = findProtected(allVars)
            for var in variables:
                # check call with protected "..._protected()..."
                #if (
                #    protectedIsLastVarChild(var) and   # like "this.a.b._protected()", not "this.a._protected.b()"
                #    var.hasParent() and var.parent.type == "operand" and  # parent is "operand"
                #    var.parent.hasParent() and var.parent.parent.type == "call"  # grandparent is "call"
                #    ):   # it's ok as method call
                #    pass
                #else:
                #self.log(var, "Protected data field in '%s'. Protected data fields are deprecated. Better use private fields in combination with getter and setter methods." % treeutil.assembleVariable(var)[0])
                pass  # protected data fields are ok
            return

        def checkImplicit(allVars):

            def hasUndeclaredMember(fullName):
                allIdentifier = fullName.split('.')
                first = second = None
                if len(allIdentifier) > 0:
                    first  = allIdentifier[0]
                if len(allIdentifier) > 1:
                    second = allIdentifier[1]
                return (first and
                        (first == "this" or first == "that") and 
                        second and
                        second not in restricted)     # <- this is bogus, too narrow
                
            for var in allVars:
                fullName = treeutil.assembleVariable(var)[0]
                if hasUndeclaredMember(fullName):
                    self.log(var, "Undeclared local data field in '%s'! You should list this field in the member section." % fullName)

            return

        def checkAll():

            def findVariables(rootNode):
                variables = []
                for node in treeutil.nodeIterator(rootNode, ["assignment", "call"]):
                    if node.type == "assignment":
                        variables.append(node.getChild("left"))
                    elif node.type == "call":
                        variables.append(node.getChild("operand"))
                return variables

            variables = findVariables(classMap["members"])
            if "construct" in classMap:
                variables.extend(findVariables(classMap["construct"]))

            for node in variables:
                this = treeutil.selectNode(node, "variable/identifier[1]/@name")
                if this != "this":
                    continue

                field = treeutil.selectNode(node, "variable/identifier[2]/@name")
                if field is None:
                    continue

                if field[0] != "_":
                    continue
                elif field[1] == "_":
                    prot = "private"
                else:
                    prot = "protected"

                if prot == "protected":
                    #self.log(node, "Protected data field '%s'. Protected fields are deprecated. Better use private fields in combination with getter and setter methods." % field)
                    pass # protected data fields are ok
                elif not field in restricted:
                    self.log(node, "Implicit declaration of %s field '%s'. You should list this field in the members section." % (prot, field))

        classMap   = self._getClassMap()
        #print self.tree.toXml()
        if len(classMap) == 0:
            return
        restricted = [key for key in self._getMembersMap() if key.startswith("_")]
        allVars    = getVariables()
        
        #checkImplicit(allVars)  # this check is overgenerating, doesn't honor all members/statics, nor inherited
        checkPrivate(allVars)
        checkProtected(allVars)
        #checkAll()


    def checkReferenceFields(self):
        members = self._getMembersMap()
        for name in members:
            valueNode = members[name].children[0]
            if valueNode.type in ["map", "instantiation", "array"]:
                if self._shouldPrintReferenceFieldWarning(valueNode, name):
                    self.log(
                        valueNode,
                        ("Data field '%s' has a reference value. " +
                        "If data fields are initialized in the members map with " +
                        "reference values like arrays or maps they will be shared " + 
                        "between all instances of the class. Usually it is better " +
                        "to set the value to 'null' and initialize it in the constructor") % name
                    )
        

    def _getClassMap(self):
        define = treeutil.findQxDefine(self.tree)
        if not define:
            return {}

        classMapNode = treeutil.selectNode(define, "params/2")
        if classMapNode is None:
            return {}

        classMap = treeutil.mapNodeToMap(classMapNode)
        return classMap
        

    def _getMembersMap(self):
        classMap = self._getClassMap()
        if not "members" in classMap:
            return {}

        members = treeutil.mapNodeToMap(classMap["members"].children[0])
        return members
        

    def checkUnusedVariables(self):
        for scope in self.script.iterScopes():
            if scope.type != Scope.EXCEPTION:
                for var in scope.variables:
                    if len(var.uses) == 0:
                        for node in var.nodes:
                            if self._shouldPrintUnusedWarning(node, var.name):
                                self.log(node, "Unused identifier '%s'" % var.name)

    
    def checkMultiDefinedVariables(self):
        for scope in self.script.iterScopes():
            if scope.type != Scope.EXCEPTION:
                for var in scope.variables:
                    if len(var.nodes) > 1:
                        for node in var.nodes:
                            self.log(node, "Multiply declared identifier '%s'" % var.name)


    DEPRECATED_IDENTIFIER = set([
        "alert",
        "confirm",
        "debugger",
        "eval"
    ])

    def isBadGlobal(self, identifier):
        return identifier in Lint.DEPRECATED_IDENTIFIER

    KNOWN_IDENTIFIER = set(lang.GLOBALS)

    def isGoodGlobal(self, identifier):
        return identifier in Lint.KNOWN_IDENTIFIER

    def checkUndefinedVariables(self, globals):
        
        # check whether this is a qooxdoo class and extract the top level namespace
        define = treeutil.findQxDefine(self.tree)
        if define:
            className = treeutil.selectNode(define, "params/1").get("value")
            globals.append(className.split(".")[0])        
        
        globalScope = self.script.getGlobalScope()
        for scope in self.script.iterScopes():
            for use in scope.uses:

                if use.name in globals:
                    continue

                if not use.definition:
                    if self.isBadGlobal(use.name) and self._shouldPrintDeprecatedWarning(use.node, use.name):
                        self.log(use.node, "Use of deprecated global identifier '%s'" % use.name)
                    elif not self.isBadGlobal(use.name) and not self.isGoodGlobal(use.name) and self._shouldPrintUndefinedWarning(use.node, use.name):
                        self.log(use.node, "Use of undefined or global identifier '%s'" % use.name)

                elif use.definition.scope == globalScope and self._shouldPrintUndefinedWarning(use.node, use.name):
                    self.log(use.node, "Use of global identifier '%s'" % use.name)


    def _shouldPrintDeprecatedWarning(self, node, name):
        return self._shouldPrintVariableWarning(node, "ignoreDeprecated", name)
    
    def _shouldPrintUndefinedWarning(self, node, name):
        return self._shouldPrintVariableWarning(node, "ignoreUndefined", name)
        
    def _shouldPrintUnusedWarning(self, node, name):
        return self._shouldPrintVariableWarning(node, "ignoreUnused", name)
    
    def _shouldPrintReferenceFieldWarning(self, node, name):
        return self._shouldPrintVariableWarning(node, "ignoreReferenceField", name)
    
    def _shouldPrintVariableWarning(self, node, docCommand, variableName):
        comments = comment.findComment(node)
        if comments is None:
            return True
        
        lintAttribs = [x for x in comments if x["category"] == "lint"]
        
        unused_re = re.compile("<p>\s*%s\s*\(\s*((?:[\w\$]+)\s*(?:,\s*(?:[\w\$]+)\s*)*)\)" % docCommand)
        for attrib in lintAttribs:
            match = unused_re.match(attrib["text"])
            if match:
                variables = [var.strip() for var in match.group(1).split(",")]
                return not variableName in variables            
        return True
Example #9
0
class Lint:
    def __init__(self, filename, logger=None):
        self.filename = filename
        content = filetool.read(filename)

        self.tree = treegenerator.createSyntaxTree(
            tokenizer.parseStream(content))
        self.script = Script(self.tree, self.filename)
        if not logger:
            self.logger = ConsoleLogger()
        else:
            self.logger = logger

    def log(self, node, msg):
        (row, column) = treeutil.getLineAndColumnFromSyntaxItem(node)
        self.logger.log(self.filename, row, column, msg)

    def checkRequiredBlocks(self):
        for node in treeutil.nodeIterator(self.tree, "loop"):
            block = treeutil.selectNode(node, "statement/block")
            if not block:
                self.log(
                    node,
                    "The statement of loops and conditions should be enclosed by a block in braces '{}'"
                )
        for node in treeutil.nodeIterator(self.tree, "elseStatement"):
            block = treeutil.selectNode(node, "block")
            if not block:
                block = treeutil.selectNode(node, "loop[@loopType='IF']")
            if not block:
                self.log(
                    node,
                    "The statement of loops and conditions should be enclosed by a block in braces '{}'"
                )

    def checkMaps(self):
        for node in treeutil.nodeIterator(self.tree, "map"):
            knownkeys = {}
            if node.hasChildren():
                for child in node.children:
                    if child.type == "keyvalue":
                        key = child.get("key")
                        if key in knownkeys:
                            self.log(child, "Map key '%s' redefined." % key)
                        else:
                            knownkeys[key] = child

    def checkFields(self):
        def getVariables():
            # get all variable nodes from 'members' and (pot.) 'construct'
            variables = []
            if "members" in classMap:
                variables.extend([
                    node for node in treeutil.nodeIterator(
                        classMap["members"], ["variable"])
                ])
            if "construct" in classMap:
                variables.extend([
                    node for node in treeutil.nodeIterator(
                        classMap["construct"], ["variable"])
                ])
            return variables

        def checkPrivate(allVars):

            privateElement = re.compile(r'\b__')

            def findPrivate(allVars):
                variables = []
                for node in allVars:
                    fullName, isComplete = treeutil.assembleVariable(node)
                    if privateElement.search(fullName):
                        variables.append(node)
                return variables

            def isLocalPrivate(var, fullName):
                allIdentifier = fullName.split('.')
                first = second = None
                if len(allIdentifier) > 0:
                    first = allIdentifier[0]
                if len(allIdentifier) > 1:
                    second = allIdentifier[1]
                return (first and (first == "this" or first == "that")
                        and second and privateElement.match(second))

            variables = findPrivate(allVars)
            for var in variables:
                fullName = treeutil.assembleVariable(var)[0]
                if isLocalPrivate(var, fullName) and fullName.split(
                        '.'
                )[1] not in restricted:  # local privates are ok, as long as they are declared
                    self.log(
                        var,
                        "Undeclared private data field '%s'. You should list this field in the members section."
                        % fullName)
            return

        def checkProtected(allVars):

            protectedElement = re.compile(r'\b_[^_]')

            def findProtected(allVars):
                variables = []
                for node in allVars:
                    # only check protected in lval position
                    if (node.hasParent() and node.parent.type == "left"
                            and node.parent.hasParent()
                            and node.parent.parent.type == "assignment"
                            and protectedElement.search(
                                treeutil.assembleVariable(node)[0])):
                        variables.append(node)
                return variables

            def protectedIsLastVarChild(var):
                lastChild = var.getLastChild(
                    ignoreComments=True)  # like "this.a.b" -> b
                if lastChild.type != "identifier":  # rules out this.a._prot[0] which isn't a call anyway
                    return False
                name = treeutil.selectNode(lastChild, "@name")
                if name and protectedElement.match(name):
                    return True
                else:
                    return False

            variables = findProtected(allVars)
            for var in variables:
                # check call with protected "..._protected()..."
                #if (
                #    protectedIsLastVarChild(var) and   # like "this.a.b._protected()", not "this.a._protected.b()"
                #    var.hasParent() and var.parent.type == "operand" and  # parent is "operand"
                #    var.parent.hasParent() and var.parent.parent.type == "call"  # grandparent is "call"
                #    ):   # it's ok as method call
                #    pass
                #else:
                #self.log(var, "Protected data field in '%s'. Protected data fields are deprecated. Better use private fields in combination with getter and setter methods." % treeutil.assembleVariable(var)[0])
                pass  # protected data fields are ok
            return

        def checkImplicit(allVars):
            def hasUndeclaredMember(fullName):
                allIdentifier = fullName.split('.')
                first = second = None
                if len(allIdentifier) > 0:
                    first = allIdentifier[0]
                if len(allIdentifier) > 1:
                    second = allIdentifier[1]
                return (first and (first == "this" or first == "that")
                        and second and second not in restricted
                        )  # <- this is bogus, too narrow

            for var in allVars:
                fullName = treeutil.assembleVariable(var)[0]
                if hasUndeclaredMember(fullName):
                    self.log(
                        var,
                        "Undeclared local data field in '%s'! You should list this field in the member section."
                        % fullName)

            return

        def checkAll():
            def findVariables(rootNode):
                variables = []
                for node in treeutil.nodeIterator(rootNode,
                                                  ["assignment", "call"]):
                    if node.type == "assignment":
                        variables.append(node.getChild("left"))
                    elif node.type == "call":
                        variables.append(node.getChild("operand"))
                return variables

            variables = findVariables(classMap["members"])
            if "construct" in classMap:
                variables.extend(findVariables(classMap["construct"]))

            for node in variables:
                this = treeutil.selectNode(node,
                                           "variable/identifier[1]/@name")
                if this != "this":
                    continue

                field = treeutil.selectNode(node,
                                            "variable/identifier[2]/@name")
                if field is None:
                    continue

                if field[0] != "_":
                    continue
                elif field[1] == "_":
                    prot = "private"
                else:
                    prot = "protected"

                if prot == "protected":
                    #self.log(node, "Protected data field '%s'. Protected fields are deprecated. Better use private fields in combination with getter and setter methods." % field)
                    pass  # protected data fields are ok
                elif not field in restricted:
                    self.log(
                        node,
                        "Implicit declaration of %s field '%s'. You should list this field in the members section."
                        % (prot, field))

        classMap = self._getClassMap()
        #print self.tree.toXml()
        if len(classMap) == 0:
            return
        restricted = [
            key for key in self._getMembersMap() if key.startswith("_")
        ]
        allVars = getVariables()

        #checkImplicit(allVars)  # this check is overgenerating, doesn't honor all members/statics, nor inherited
        checkPrivate(allVars)
        checkProtected(allVars)
        #checkAll()

    def checkReferenceFields(self):
        members = self._getMembersMap()
        for name in members:
            valueNode = members[name].children[0]
            if valueNode.type in ["map", "instantiation", "array"]:
                if self._shouldPrintReferenceFieldWarning(valueNode, name):
                    self.log(valueNode, (
                        "Data field '%s' has a reference value. " +
                        "If data fields are initialized in the members map with "
                        +
                        "reference values like arrays or maps they will be shared "
                        +
                        "between all instances of the class. Usually it is better "
                        +
                        "to set the value to 'null' and initialize it in the constructor"
                    ) % name)

    def _getClassMap(self):
        define = treeutil.findQxDefine(self.tree)
        if not define:
            return {}

        classMapNode = treeutil.selectNode(define, "params/2")
        if classMapNode is None:
            return {}

        classMap = treeutil.mapNodeToMap(classMapNode)
        return classMap

    def _getMembersMap(self):
        classMap = self._getClassMap()
        if not "members" in classMap:
            return {}

        members = treeutil.mapNodeToMap(classMap["members"].children[0])
        return members

    def checkUnusedVariables(self):
        for scope in self.script.iterScopes():
            if scope.type != Scope.EXCEPTION:
                for var in scope.variables:
                    if len(var.uses) == 0:
                        for node in var.nodes:
                            if self._shouldPrintUnusedWarning(node, var.name):
                                self.log(node,
                                         "Unused identifier '%s'" % var.name)

    def checkMultiDefinedVariables(self):
        for scope in self.script.iterScopes():
            if scope.type != Scope.EXCEPTION:
                for var in scope.variables:
                    if len(var.nodes) > 1:
                        for node in var.nodes:
                            self.log(
                                node,
                                "Multiply declared identifier '%s'" % var.name)

    DEPRECATED_IDENTIFIER = set(["alert", "confirm", "debugger", "eval"])

    def isBadGlobal(self, identifier):
        return identifier in Lint.DEPRECATED_IDENTIFIER

    KNOWN_IDENTIFIER = set(lang.GLOBALS)

    def isGoodGlobal(self, identifier):
        return identifier in Lint.KNOWN_IDENTIFIER

    def checkUndefinedVariables(self, globals):

        # check whether this is a qooxdoo class and extract the top level namespace
        define = treeutil.findQxDefine(self.tree)
        if define:
            className = treeutil.selectNode(define, "params/1").get("value")
            globals.append(className.split(".")[0])

        globalScope = self.script.getGlobalScope()
        for scope in self.script.iterScopes():
            for use in scope.uses:

                if use.name in globals:
                    continue

                if not use.definition:
                    if self.isBadGlobal(
                            use.name) and self._shouldPrintDeprecatedWarning(
                                use.node, use.name):
                        self.log(
                            use.node,
                            "Use of deprecated global identifier '%s'" %
                            use.name)
                    elif not self.isBadGlobal(
                            use.name) and not self.isGoodGlobal(
                                use.name
                            ) and self._shouldPrintUndefinedWarning(
                                use.node, use.name):
                        self.log(
                            use.node,
                            "Use of undefined or global identifier '%s'" %
                            use.name)

                elif use.definition.scope == globalScope and self._shouldPrintUndefinedWarning(
                        use.node, use.name):
                    self.log(use.node,
                             "Use of global identifier '%s'" % use.name)

    def _shouldPrintDeprecatedWarning(self, node, name):
        return self._shouldPrintVariableWarning(node, "ignoreDeprecated", name)

    def _shouldPrintUndefinedWarning(self, node, name):
        return self._shouldPrintVariableWarning(node, "ignoreUndefined", name)

    def _shouldPrintUnusedWarning(self, node, name):
        return self._shouldPrintVariableWarning(node, "ignoreUnused", name)

    def _shouldPrintReferenceFieldWarning(self, node, name):
        return self._shouldPrintVariableWarning(node, "ignoreReferenceField",
                                                name)

    def _shouldPrintVariableWarning(self, node, docCommand, variableName):
        comments = comment.findComment(node)
        if comments is None:
            return True

        lintAttribs = [x for x in comments if x["category"] == "lint"]

        unused_re = re.compile(
            "<p>\s*%s\s*\(\s*((?:[\w\$]+)\s*(?:,\s*(?:[\w\$]+)\s*)*)\)" %
            docCommand)
        for attrib in lintAttribs:
            match = unused_re.match(attrib["text"])
            if match:
                variables = [var.strip() for var in match.group(1).split(",")]
                return not variableName in variables
        return True